ipv6 fix, race condition fix, varient_stream fixes

This commit is contained in:
Marcos Pinto 2007-09-22 17:50:59 +00:00
parent f62bf99941
commit f5868df3ad
17 changed files with 394 additions and 195 deletions

View File

@ -328,14 +328,33 @@ namespace libtorrent
struct TORRENT_EXPORT listen_failed_alert: alert struct TORRENT_EXPORT listen_failed_alert: alert
{ {
listen_failed_alert( listen_failed_alert(
const std::string& msg) tcp::endpoint const& ep
, std::string const& msg)
: alert(alert::fatal, msg) : alert(alert::fatal, msg)
, endpoint(ep)
{} {}
tcp::endpoint endpoint;
virtual std::auto_ptr<alert> clone() const virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new listen_failed_alert(*this)); } { return std::auto_ptr<alert>(new listen_failed_alert(*this)); }
}; };
struct TORRENT_EXPORT listen_succeeded_alert: alert
{
listen_succeeded_alert(
tcp::endpoint const& ep
, std::string const& msg)
: alert(alert::fatal, msg)
, endpoint(ep)
{}
tcp::endpoint endpoint;
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new listen_succeeded_alert(*this)); }
};
struct TORRENT_EXPORT portmap_error_alert: alert struct TORRENT_EXPORT portmap_error_alert: alert
{ {
portmap_error_alert(const std::string& msg) portmap_error_alert(const std::string& msg)

View File

@ -189,11 +189,16 @@ namespace libtorrent
#endif #endif
void operator()(); void operator()();
void open_listen_port(); void open_listen_port() throw();
void async_accept(); // if we are listening on an IPv6 interface
// this will return one of the IPv6 addresses on this
// machine, otherwise just an empty endpoint
tcp::endpoint get_ipv6_interface() const;
void async_accept(boost::shared_ptr<socket_acceptor> const& listener);
void on_incoming_connection(boost::shared_ptr<socket_type> const& s void on_incoming_connection(boost::shared_ptr<socket_type> const& s
, boost::weak_ptr<socket_acceptor> const& as, asio::error_code const& e); , boost::weak_ptr<socket_acceptor> listener, asio::error_code const& e);
// must be locked to access the data // must be locked to access the data
// in this struct // in this struct
@ -393,8 +398,10 @@ namespace libtorrent
// at startup // at startup
int m_key; int m_key;
// the range of ports we try to listen on // the number of retries we make when binding the
std::pair<int, int> m_listen_port_range; // listen socket. For each retry the port number
// is incremented by one
int m_listen_port_retries;
// the ip-address of the interface // the ip-address of the interface
// we are supposed to listen on. // we are supposed to listen on.
@ -403,16 +410,31 @@ namespace libtorrent
// interface to listen on // interface to listen on
tcp::endpoint m_listen_interface; tcp::endpoint m_listen_interface;
// this is typically set to the same as the local // if we're listening on an IPv6 interface
// listen port. In case a NAT port forward was // this is one of the non local IPv6 interfaces
// successfully opened, this will be set to the // on this machine
// port that is open on the external (NAT) interface tcp::endpoint m_ipv6_interface;
// on the NAT box itself. This is the port that has
// to be published to peers, since this is the port
// the client is reachable through.
int m_external_listen_port;
boost::shared_ptr<socket_acceptor> m_listen_socket; struct listen_socket_t
{
listen_socket_t(): external_port(0) {}
// this is typically set to the same as the local
// listen port. In case a NAT port forward was
// successfully opened, this will be set to the
// port that is open on the external (NAT) interface
// on the NAT box itself. This is the port that has
// to be published to peers, since this is the port
// the client is reachable through.
int external_port;
// the actual socket
boost::shared_ptr<socket_acceptor> sock;
};
// since we might be listening on multiple interfaces
// we might need more than one listen socket
std::list<listen_socket_t> m_listen_sockets;
listen_socket_t setup_listener(tcp::endpoint ep, int retries);
// the settings for the client // the settings for the client
session_settings m_settings; session_settings m_settings;

View File

@ -44,6 +44,7 @@ namespace libtorrent
bool is_local(address const& a); bool is_local(address const& a);
bool is_loopback(address const& addr); bool is_loopback(address const& addr);
bool is_multicast(address const& addr); bool is_multicast(address const& addr);
bool is_any(address const& addr);
address_v4 guess_local_address(asio::io_service&); address_v4 guess_local_address(asio::io_service&);

View File

@ -37,7 +37,7 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent namespace libtorrent
{ {
std::vector<address> const& enum_net_interfaces(asio::io_service& ios, asio::error_code& ec); std::vector<address> enum_net_interfaces(asio::io_service& ios, asio::error_code& ec);
} }
#endif #endif

View File

@ -73,6 +73,8 @@ namespace libtorrent
T header(char const* key) const; T header(char const* key) const;
std::string const& protocol() const { return m_protocol; } std::string const& protocol() const { return m_protocol; }
int status_code() const { return m_status_code; } int status_code() const { return m_status_code; }
std::string const& method() const { return m_method; }
std::string const& path() const { return m_path; }
std::string message() const { return m_server_message; } std::string message() const { return m_server_message; }
buffer::const_interval get_body() const; buffer::const_interval get_body() const;
bool header_finished() const { return m_state == read_body; } bool header_finished() const { return m_state == read_body; }
@ -85,6 +87,8 @@ namespace libtorrent
private: private:
int m_recv_pos; int m_recv_pos;
int m_status_code; int m_status_code;
std::string m_method;
std::string m_path;
std::string m_protocol; std::string m_protocol;
std::string m_server_message; std::string m_server_message;

View File

@ -98,6 +98,16 @@ namespace libtorrent
typedef asio::basic_deadline_timer<libtorrent::ptime> deadline_timer; typedef asio::basic_deadline_timer<libtorrent::ptime> deadline_timer;
inline std::ostream& operator<<(std::ostream& os, tcp::endpoint const& ep)
{
address const& a = ep.address();
if (a.is_v6())
os << "[" << a.to_string() << "]:" << ep.port();
else
os << a.to_string() << ":" << ep.port();
return os;
}
namespace detail namespace detail
{ {
template<class OutIt> template<class OutIt>

View File

@ -113,6 +113,7 @@ namespace libtorrent
std::string url; std::string url;
int key; int key;
int num_want; int num_want;
std::string ipv6;
}; };
struct TORRENT_EXPORT request_callback struct TORRENT_EXPORT request_callback

View File

@ -232,18 +232,18 @@ namespace aux
// -------------- remote_endpoint ----------- // -------------- remote_endpoint -----------
template <class EndpointType, class Error_Handler = boost::mpl::void_> template <class EndpointType>
struct remote_endpoint_visitor struct remote_endpoint_visitor_ec
: boost::static_visitor<EndpointType> : boost::static_visitor<EndpointType>
{ {
remote_endpoint_visitor(Error_Handler const& error_handler) remote_endpoint_visitor_ec(asio::error_code& ec)
: error_handler(error_handler) : error_code(ec)
{} {}
template <class T> template <class T>
EndpointType operator()(T* p) const EndpointType operator()(T* p) const
{ {
return p->remote_endpoint(error_handler); return p->remote_endpoint(error_code);
} }
EndpointType operator()(boost::blank) const EndpointType operator()(boost::blank) const
@ -251,11 +251,11 @@ namespace aux
return EndpointType(); return EndpointType();
} }
Error_Handler const& error_handler; asio::error_code& error_code;
}; };
template <class EndpointType> template <class EndpointType>
struct remote_endpoint_visitor<EndpointType, boost::mpl::void_> struct remote_endpoint_visitor
: boost::static_visitor<EndpointType> : boost::static_visitor<EndpointType>
{ {
template <class T> template <class T>
@ -272,18 +272,18 @@ namespace aux
// -------------- local_endpoint ----------- // -------------- local_endpoint -----------
template <class EndpointType, class Error_Handler = boost::mpl::void_> template <class EndpointType>
struct local_endpoint_visitor struct local_endpoint_visitor_ec
: boost::static_visitor<EndpointType> : boost::static_visitor<EndpointType>
{ {
local_endpoint_visitor(Error_Handler const& error_handler) local_endpoint_visitor_ec(asio::error_code& ec)
: error_handler(error_handler) : error_code(ec)
{} {}
template <class T> template <class T>
EndpointType operator()(T* p) const EndpointType operator()(T* p) const
{ {
return p->local_endpoint(error_handler); return p->local_endpoint(error_code);
} }
EndpointType operator()(boost::blank) const EndpointType operator()(boost::blank) const
@ -291,11 +291,11 @@ namespace aux
return EndpointType(); return EndpointType();
} }
Error_Handler const& error_handler; asio::error_code& error_code;
}; };
template <class EndpointType> template <class EndpointType>
struct local_endpoint_visitor<EndpointType, boost::mpl::void_> struct local_endpoint_visitor
: boost::static_visitor<EndpointType> : boost::static_visitor<EndpointType>
{ {
template <class T> template <class T>
@ -665,12 +665,11 @@ public:
return boost::apply_visitor(aux::remote_endpoint_visitor<endpoint_type>(), m_variant); return boost::apply_visitor(aux::remote_endpoint_visitor<endpoint_type>(), m_variant);
} }
template <class Error_Handler> endpoint_type remote_endpoint(asio::error_code& ec)
endpoint_type remote_endpoint(Error_Handler const& error_handler)
{ {
assert(instantiated()); assert(instantiated());
return boost::apply_visitor( return boost::apply_visitor(
aux::remote_endpoint_visitor<endpoint_type, Error_Handler>(error_handler), m_variant aux::remote_endpoint_visitor_ec<endpoint_type>(ec), m_variant
); );
} }
@ -680,12 +679,11 @@ public:
return boost::apply_visitor(aux::local_endpoint_visitor<endpoint_type>(), m_variant); return boost::apply_visitor(aux::local_endpoint_visitor<endpoint_type>(), m_variant);
} }
template <class Error_Handler> endpoint_type local_endpoint(asio::error_code& ec)
endpoint_type local_endpoint(Error_Handler const& error_handler)
{ {
assert(instantiated()); assert(instantiated());
return boost::apply_visitor( return boost::apply_visitor(
aux::local_endpoint_visitor<endpoint_type, Error_Handler>(error_handler), m_variant aux::local_endpoint_visitor_ec<endpoint_type>(ec), m_variant
); );
} }

View File

@ -65,7 +65,7 @@ namespace libtorrent {
alert_manager::alert_manager() alert_manager::alert_manager()
: m_severity(alert::none) : m_severity(alert::fatal)
{} {}
alert_manager::~alert_manager() alert_manager::~alert_manager()

View File

@ -84,6 +84,14 @@ namespace libtorrent
return addr.to_v6().is_multicast(); return addr.to_v6().is_multicast();
} }
bool is_any(address const& addr)
{
if (addr.is_v4())
return addr.to_v4() == address_v4::any();
else
return addr.to_v6() == address_v6::any();
}
broadcast_socket::broadcast_socket(asio::io_service& ios broadcast_socket::broadcast_socket(asio::io_service& ios
, udp::endpoint const& multicast_endpoint , udp::endpoint const& multicast_endpoint
, receive_handler_t const& handler , receive_handler_t const& handler

View File

@ -50,7 +50,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/version.hpp" #include "libtorrent/version.hpp"
#include "libtorrent/extensions.hpp" #include "libtorrent/extensions.hpp"
#include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/aux_/session_impl.hpp"
#include "libtorrent/enum_net.hpp"
#ifndef TORRENT_DISABLE_ENCRYPTION #ifndef TORRENT_DISABLE_ENCRYPTION
#include "libtorrent/pe_crypto.hpp" #include "libtorrent/pe_crypto.hpp"
@ -1475,18 +1474,14 @@ namespace libtorrent
detail::write_address(remote().address(), out); detail::write_address(remote().address(), out);
handshake["yourip"] = remote_address; handshake["yourip"] = remote_address;
handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue; handshake["reqq"] = m_ses.settings().max_allowed_in_request_queue;
asio::error_code ec;
std::vector<address> const& interfaces = enum_net_interfaces(get_socket()->io_service(), ec); tcp::endpoint ep = m_ses.get_ipv6_interface();
for (std::vector<address>::const_iterator i = interfaces.begin() if (ep != tcp::endpoint())
, end(interfaces.end()); i != end; ++i)
{ {
// TODO: only use global IPv6 addresses
if (!i->is_v6() || i->to_v6().is_link_local()) continue;
std::string ipv6_address; std::string ipv6_address;
std::back_insert_iterator<std::string> out(ipv6_address); std::back_insert_iterator<std::string> out(ipv6_address);
detail::write_address(*i, out); detail::write_address(ep.address(), out);
handshake["ipv6"] = ipv6_address; handshake["ipv6"] = ipv6_address;
break;
} }
// loop backwards, to make the first extension be the last // loop backwards, to make the first extension be the last

View File

@ -40,10 +40,9 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent namespace libtorrent
{ {
std::vector<address> const& enum_net_interfaces(asio::io_service& ios, asio::error_code& ec) std::vector<address> enum_net_interfaces(asio::io_service& ios, asio::error_code& ec)
{ {
static std::vector<address> ret; std::vector<address> ret;
if (!ret.empty()) return ret;
#if defined __linux__ || defined __MACH__ || defined(__FreeBSD__) #if defined __linux__ || defined __MACH__ || defined(__FreeBSD__)
int s = socket(AF_INET, SOCK_DGRAM, 0); int s = socket(AF_INET, SOCK_DGRAM, 0);

View File

@ -148,13 +148,19 @@ namespace libtorrent
pos = newline; pos = newline;
line >> m_protocol; line >> m_protocol;
if (m_protocol.substr(0, 5) != "HTTP/") if (m_protocol.substr(0, 5) == "HTTP/")
{ {
throw std::runtime_error("unknown protocol in HTTP response: " line >> m_status_code;
+ m_protocol + " line: " + std::string(pos, newline)); std::getline(line, m_server_message);
}
else
{
m_method = m_protocol;
std::transform(m_method.begin(), m_method.end(), m_method.begin(), &to_lower);
m_protocol.clear();
line >> m_path >> m_protocol;
m_status_code = 0;
} }
line >> m_status_code;
std::getline(line, m_server_message);
m_state = read_header; m_state = read_header;
} }
@ -422,6 +428,12 @@ namespace libtorrent
m_send_buffer += "supportcrypto=1&"; m_send_buffer += "supportcrypto=1&";
#endif #endif
if (!url_has_argument(request, "ipv6") && !req.ipv6.empty())
{
m_send_buffer += "ipv6=";
m_send_buffer += req.ipv6;
}
// extension that tells the tracker that // extension that tells the tracker that
// we don't need any peer_id's in the response // we don't need any peer_id's in the response
if (!url_has_argument(request, "no_peer_id")) if (!url_has_argument(request, "no_peer_id"))

View File

@ -162,6 +162,18 @@ namespace
tcp::endpoint const& m_ep; tcp::endpoint const& m_ep;
}; };
struct match_peer_id
{
match_peer_id(peer_id const& id_)
: m_id(id_)
{}
bool operator()(policy::peer const& p) const
{ return p.connection && p.connection->pid() == m_id; }
peer_id const& m_id;
};
struct match_peer_connection struct match_peer_connection
{ {
match_peer_connection(peer_connection const& c) match_peer_connection(peer_connection const& c)
@ -1030,6 +1042,12 @@ namespace libtorrent
m_peers.begin() m_peers.begin()
, m_peers.end() , m_peers.end()
, match_peer_endpoint(remote)); , match_peer_endpoint(remote));
if (i == m_peers.end())
i = std::find_if(
m_peers.begin()
, m_peers.end()
, match_peer_id(pid));
} }
else else
{ {
@ -1156,7 +1174,7 @@ namespace libtorrent
// data from now on // data from now on
void policy::unchoked(peer_connection& c) void policy::unchoked(peer_connection& c)
{ {
INVARIANT_CHECK; // INVARIANT_CHECK;
if (c.is_interesting()) if (c.is_interesting())
{ {
request_a_block(*m_torrent, c); request_a_block(*m_torrent, c);
@ -1166,7 +1184,7 @@ namespace libtorrent
// called when a peer is interested in us // called when a peer is interested in us
void policy::interested(peer_connection& c) void policy::interested(peer_connection& c)
{ {
INVARIANT_CHECK; // INVARIANT_CHECK;
assert(std::find_if(m_peers.begin(), m_peers.end() assert(std::find_if(m_peers.begin(), m_peers.end()
, boost::bind<bool>(std::equal_to<peer_connection*>(), bind(&peer::connection, _1) , boost::bind<bool>(std::equal_to<peer_connection*>(), bind(&peer::connection, _1)
@ -1278,7 +1296,7 @@ namespace libtorrent
*/ */
bool policy::connect_one_peer() bool policy::connect_one_peer()
{ {
INVARIANT_CHECK; // INVARIANT_CHECK;
assert(m_torrent->want_more_peers()); assert(m_torrent->want_more_peers());
@ -1291,7 +1309,6 @@ namespace libtorrent
try try
{ {
INVARIANT_CHECK;
p->connected = time_now(); p->connected = time_now();
p->connection = m_torrent->connect_to_peer(&*p); p->connection = m_torrent->connect_to_peer(&*p);
assert(p->connection == m_torrent->connection_for(p->ip)); assert(p->connection == m_torrent->connection_for(p->ip));
@ -1375,7 +1392,7 @@ namespace libtorrent
void policy::peer_is_interesting(peer_connection& c) void policy::peer_is_interesting(peer_connection& c)
{ {
INVARIANT_CHECK; // INVARIANT_CHECK;
c.send_interested(); c.send_interested();
if (c.has_peer_choked() if (c.has_peer_choked()

View File

@ -73,6 +73,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/socket.hpp" #include "libtorrent/socket.hpp"
#include "libtorrent/aux_/session_impl.hpp" #include "libtorrent/aux_/session_impl.hpp"
#include "libtorrent/kademlia/dht_tracker.hpp" #include "libtorrent/kademlia/dht_tracker.hpp"
#include "libtorrent/enum_net.hpp"
#ifndef TORRENT_DISABLE_ENCRYPTION #ifndef TORRENT_DISABLE_ENCRYPTION
@ -512,9 +513,8 @@ namespace detail
, m_download_channel(m_io_service, peer_connection::download_channel) , m_download_channel(m_io_service, peer_connection::download_channel)
, m_upload_channel(m_io_service, peer_connection::upload_channel) , m_upload_channel(m_io_service, peer_connection::upload_channel)
, m_tracker_manager(m_settings, m_tracker_proxy) , m_tracker_manager(m_settings, m_tracker_proxy)
, m_listen_port_range(listen_port_range) , m_listen_port_retries(listen_port_range.second - listen_port_range.first)
, m_listen_interface(address::from_string(listen_interface), listen_port_range.first) , m_listen_interface(address::from_string(listen_interface), listen_port_range.first)
, m_external_listen_port(0)
, m_abort(false) , m_abort(false)
, m_max_uploads(8) , m_max_uploads(8)
, m_max_connections(200) , m_max_connections(200)
@ -661,129 +661,223 @@ namespace detail
*i = ' '; *i = ' ';
} }
void session_impl::open_listen_port() tcp::endpoint session_impl::get_ipv6_interface() const
{ {
try return m_ipv6_interface;
{ }
// create listener socket
m_listen_socket.reset(new socket_acceptor(m_io_service));
for(;;) session_impl::listen_socket_t session_impl::setup_listener(tcp::endpoint ep, int retries)
{ {
try asio::error_code ec;
{ listen_socket_t s;
m_listen_socket->open(m_listen_interface.protocol()); s.sock.reset(new socket_acceptor(m_io_service));
m_listen_socket->set_option(socket_acceptor::reuse_address(true)); s.sock->open(ep.protocol(), ec);
m_listen_socket->bind(m_listen_interface); s.sock->set_option(socket_acceptor::reuse_address(true), ec);
m_listen_socket->listen(); s.sock->bind(ep, ec);
m_listen_interface = m_listen_socket->local_endpoint(); while (ec && retries > 0)
m_external_listen_port = m_listen_interface.port(); {
break; ec = asio::error_code();
} assert(!ec);
catch (asio::system_error& e) --retries;
{ ep.port(ep.port() + 1);
// TODO: make sure this is correct s.sock->bind(ep, ec);
if (e.code() == asio::error::host_not_found
|| m_listen_interface.port() == 0)
{
if (m_alerts.should_post(alert::fatal))
{
std::string msg = "cannot listen on the given interface '"
+ m_listen_interface.address().to_string() + "'";
m_alerts.post_alert(listen_failed_alert(msg));
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
std::string msg = "cannot listen on the given interface '"
+ m_listen_interface.address().to_string() + "'";
(*m_logger) << msg << "\n";
#endif
assert(m_listen_socket.unique());
m_listen_socket.reset();
break;
}
m_listen_socket->close();
m_listen_interface.port(m_listen_interface.port() + 1);
if (m_listen_interface.port() > m_listen_port_range.second)
{
std::stringstream msg;
msg << "none of the ports in the range ["
<< m_listen_port_range.first
<< ", " << m_listen_port_range.second
<< "] could be opened for listening";
m_alerts.post_alert(listen_failed_alert(msg.str()));
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << msg.str() << "\n";
#endif
m_listen_socket.reset();
break;
}
}
}
} }
catch (asio::system_error& e) if (ec)
{
// instead of giving up, try
// let the OS pick a port
ep.port(0);
ec = asio::error_code();
s.sock->bind(ep, ec);
}
if (ec)
{
// not even that worked, give up
if (m_alerts.should_post(alert::fatal))
{
std::string msg = "cannot bind to the given interface '"
+ boost::lexical_cast<std::string>(ep) + "' " + ec.message();
m_alerts.post_alert(listen_failed_alert(ep, msg));
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
std::string msg = "cannot bind to the given interface '"
+ boost::lexical_cast<std::string>(ep) + "' " + ec.message();
(*m_logger) << msg << "\n";
#endif
return listen_socket_t();
}
s.external_port = s.sock->local_endpoint(ec).port();
s.sock->listen(0, ec);
if (ec)
{ {
if (m_alerts.should_post(alert::fatal)) if (m_alerts.should_post(alert::fatal))
{ {
m_alerts.post_alert(listen_failed_alert( std::string msg = "cannot listen the given interface '"
std::string("failed to open listen port: ") + e.what())); + boost::lexical_cast<std::string>(ep) + "' " + ec.message();
m_alerts.post_alert(listen_failed_alert(ep, msg));
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
std::string msg = "cannot listen the given interface '"
+ boost::lexical_cast<std::string>(ep) + "' " + ec.message();
(*m_logger) << msg << "\n";
#endif
return listen_socket_t();
}
if (m_alerts.should_post(alert::fatal))
{
std::string msg = "listening on interface "
+ boost::lexical_cast<std::string>(ep);
m_alerts.post_alert(listen_succeeded_alert(ep, msg));
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << "listening on: " << ep.address().to_string() << ":" << ep.port()
<< " external port: " << s.external_port << "\n";
#endif
return s;
}
void session_impl::open_listen_port() throw()
{
// close the open listen sockets
m_listen_sockets.clear();
m_incoming_connection = false;
if (is_any(m_listen_interface.address()))
{
// this means we should open two listen sockets
// one for IPv4 and one for IPv6
listen_socket_t s = setup_listener(
tcp::endpoint(address_v4::any(), m_listen_interface.port())
, m_listen_port_retries);
if (s.sock)
{
m_listen_sockets.push_back(s);
async_accept(s.sock);
}
s = setup_listener(
tcp::endpoint(address_v6::any(), m_listen_interface.port())
, m_listen_port_retries);
if (s.sock)
{
m_listen_sockets.push_back(s);
async_accept(s.sock);
}
}
else
{
// we should only open a single listen socket, that
// binds to the given interface
listen_socket_t s = setup_listener(
m_listen_interface, m_listen_port_retries);
if (s.sock)
{
m_listen_sockets.push_back(s);
async_accept(s.sock);
} }
} }
if (m_listen_socket) m_ipv6_interface = tcp::endpoint();
for (std::list<listen_socket_t>::const_iterator i = m_listen_sockets.begin()
, end(m_listen_sockets.end()); i != end; ++i)
{ {
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) asio::error_code ec;
(*m_logger) << "listening on port: " << m_listen_interface.port() tcp::endpoint ep = i->sock->local_endpoint(ec);
<< " external port: " << m_external_listen_port << "\n"; if (ec || ep.address().is_v4()) continue;
#endif
async_accept(); if (ep.address().to_v6() != address_v6::any())
if (m_natpmp.get()) {
m_natpmp->set_mappings(m_listen_interface.port(), 0); // if we're listening on a specific address
if (m_upnp.get()) // pick it
m_upnp->set_mappings(m_listen_interface.port(), 0); m_ipv6_interface = ep;
}
else
{
// if we're listening on any IPv6 address, enumerate them and
// pick the first non-local address
std::vector<address> const& ifs = enum_net_interfaces(m_io_service, ec);
for (std::vector<address>::const_iterator i = ifs.begin()
, end(ifs.end()); i != end; ++i)
{
if (i->is_v4() || i->to_v6().is_link_local() || i->to_v6().is_loopback()) continue;
m_ipv6_interface = tcp::endpoint(*i, ep.port());
break;
}
break;
}
}
if (!m_listen_sockets.empty())
{
asio::error_code ec;
tcp::endpoint local = m_listen_sockets.front().sock->local_endpoint(ec);
if (!ec)
{
if (m_natpmp.get()) m_natpmp->set_mappings(local.port(), 0);
if (m_upnp.get()) m_upnp->set_mappings(local.port(), 0);
}
} }
} }
void session_impl::async_accept() void session_impl::async_accept(boost::shared_ptr<socket_acceptor> const& listener)
{ {
shared_ptr<socket_type> c(new socket_type(m_io_service)); shared_ptr<socket_type> c(new socket_type(m_io_service));
c->instantiate<stream_socket>(); c->instantiate<stream_socket>();
m_listen_socket->async_accept(c->get<stream_socket>() listener->async_accept(c->get<stream_socket>()
, bind(&session_impl::on_incoming_connection, this, c , bind(&session_impl::on_incoming_connection, this, c
, weak_ptr<socket_acceptor>(m_listen_socket), _1)); , boost::weak_ptr<socket_acceptor>(listener), _1));
} }
void session_impl::on_incoming_connection(shared_ptr<socket_type> const& s void session_impl::on_incoming_connection(shared_ptr<socket_type> const& s
, weak_ptr<socket_acceptor> const& listen_socket, asio::error_code const& e) try , weak_ptr<socket_acceptor> listen_socket, asio::error_code const& e) try
{ {
if (listen_socket.expired()) boost::shared_ptr<socket_acceptor> listener = listen_socket.lock();
return; if (!listener) return;
if (e == asio::error::operation_aborted) if (e == asio::error::operation_aborted) return;
return;
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
assert(listen_socket.lock() == m_listen_socket);
if (m_abort) return; if (m_abort) return;
asio::error_code ec;
if (e) if (e)
{ {
tcp::endpoint ep = listener->local_endpoint(ec);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
std::string msg = "error accepting connection on '" std::string msg = "error accepting connection on '"
+ m_listen_interface.address().to_string() + "'"; + boost::lexical_cast<std::string>(ep) + "' " + e.message();
(*m_logger) << msg << "\n"; (*m_logger) << msg << "\n";
#endif #endif
assert(m_listen_socket.unique()); if (m_alerts.should_post(alert::fatal))
// try any random port {
m_listen_interface.port(0); std::string msg = "error accepting connection on '"
open_listen_port(); + boost::lexical_cast<std::string>(ep) + "' " + ec.message();
m_alerts.post_alert(listen_failed_alert(ep, msg));
}
return; return;
} }
async_accept(); async_accept(listener);
// we got a connection request! // we got a connection request!
m_incoming_connection = true; m_incoming_connection = true;
tcp::endpoint endp = s->remote_endpoint(); tcp::endpoint endp = s->remote_endpoint(ec);
if (ec)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << endp << " <== INCOMING CONNECTION FAILED, could "
"not retrieve remote endpoint " << ec.message() << "\n";
#endif
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
(*m_logger) << endp << " <== INCOMING CONNECTION\n"; (*m_logger) << endp << " <== INCOMING CONNECTION\n";
@ -803,28 +897,22 @@ namespace detail
// check if we have any active torrents // check if we have any active torrents
// if we don't reject the connection // if we don't reject the connection
if (m_torrents.empty()) if (m_torrents.empty()) return;
bool has_active_torrent = false;
for (torrent_map::iterator i = m_torrents.begin()
, end(m_torrents.end()); i != end; ++i)
{ {
return; if (!i->second->is_paused())
}
else
{
bool has_active_torrent = false;
for (torrent_map::iterator i = m_torrents.begin()
, end(m_torrents.end()); i != end; ++i)
{ {
if (!i->second->is_paused()) has_active_torrent = true;
{ break;
has_active_torrent = true;
break;
}
} }
if (!has_active_torrent)
return;
} }
if (!has_active_torrent) return;
boost::intrusive_ptr<peer_connection> c( boost::intrusive_ptr<peer_connection> c(
new bt_peer_connection(*this, s, 0)); new bt_peer_connection(*this, s, 0));
#ifndef NDEBUG #ifndef NDEBUG
c->m_in_constructor = false; c->m_in_constructor = false;
#endif #endif
@ -1064,7 +1152,9 @@ namespace detail
if (t.should_request()) if (t.should_request())
{ {
tracker_request req = t.generate_tracker_request(); tracker_request req = t.generate_tracker_request();
req.listen_port = m_external_listen_port; req.listen_port = 0;
if (!m_listen_sockets.empty())
req.listen_port = m_listen_sockets.front().external_port;
req.key = m_key; req.key = m_key;
m_tracker_manager.queue_request(m_strand, m_half_open, req m_tracker_manager.queue_request(m_strand, m_half_open, req
, t.tracker_login(), m_listen_interface.address(), i->second); , t.tracker_login(), m_listen_interface.address(), i->second);
@ -1274,10 +1364,9 @@ namespace detail
{ {
eh_initializer(); eh_initializer();
if (m_listen_port_range.first != 0 && m_listen_port_range.second != 0)
{ {
session_impl::mutex_t::scoped_lock l(m_mutex); session_impl::mutex_t::scoped_lock l(m_mutex);
open_listen_port(); if (m_listen_interface.port() != 0) open_listen_port();
} }
ptime timer = time_now(); ptime timer = time_now();
@ -1331,8 +1420,10 @@ namespace detail
&& !i->second->trackers().empty()) && !i->second->trackers().empty())
{ {
tracker_request req = i->second->generate_tracker_request(); tracker_request req = i->second->generate_tracker_request();
assert(m_external_listen_port > 0); assert(!m_listen_sockets.empty());
req.listen_port = m_external_listen_port; req.listen_port = 0;
if (!m_listen_sockets.empty())
req.listen_port = m_listen_sockets.front().external_port;
req.key = m_key; req.key = m_key;
std::string login = i->second->tracker_login(); std::string login = i->second->tracker_login();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
@ -1347,8 +1438,8 @@ namespace detail
} }
} }
// close listen socket // close the listen sockets
m_listen_socket.reset(); m_listen_sockets.clear();
ptime start(time_now()); ptime start(time_now());
l.unlock(); l.unlock();
@ -1477,10 +1568,12 @@ namespace detail
, bool paused , bool paused
, void* userdata) , void* userdata)
{ {
assert(!save_path.empty());
// if you get this assert, you haven't managed to // if you get this assert, you haven't managed to
// open a listen port. call listen_on() first. // open a listen port. call listen_on() first.
assert(m_external_listen_port > 0); if (m_listen_sockets.empty())
assert(!save_path.empty()); throw std::runtime_error("no listen socket opened");
if (ti->begin_files() == ti->end_files()) if (ti->begin_files() == ti->end_files())
throw std::runtime_error("no files in torrent"); throw std::runtime_error("no files in torrent");
@ -1489,7 +1582,7 @@ namespace detail
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
mutex::scoped_lock l2(m_checker_impl.m_mutex); mutex::scoped_lock l2(m_checker_impl.m_mutex);
INVARIANT_CHECK; // INVARIANT_CHECK;
if (is_aborted()) if (is_aborted())
throw std::runtime_error("session is closing"); throw std::runtime_error("session is closing");
@ -1573,7 +1666,7 @@ namespace detail
// lock the session // lock the session
session_impl::mutex_t::scoped_lock l(m_mutex); session_impl::mutex_t::scoped_lock l(m_mutex);
INVARIANT_CHECK; // INVARIANT_CHECK;
// is the torrent already active? // is the torrent already active?
if (!find_torrent(info_hash).expired()) if (!find_torrent(info_hash).expired())
@ -1628,8 +1721,10 @@ namespace detail
{ {
tracker_request req = t.generate_tracker_request(); tracker_request req = t.generate_tracker_request();
assert(req.event == tracker_request::stopped); assert(req.event == tracker_request::stopped);
assert(m_external_listen_port > 0); assert(!m_listen_sockets.empty());
req.listen_port = m_external_listen_port; req.listen_port = 0;
if (!m_listen_sockets.empty())
req.listen_port = m_listen_sockets.front().external_port;
req.key = m_key; req.key = m_key;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
@ -1684,19 +1779,15 @@ namespace detail
if (net_interface && std::strlen(net_interface) > 0) if (net_interface && std::strlen(net_interface) > 0)
new_interface = tcp::endpoint(address::from_string(net_interface), port_range.first); new_interface = tcp::endpoint(address::from_string(net_interface), port_range.first);
else else
new_interface = tcp::endpoint(address(), port_range.first); new_interface = tcp::endpoint(address_v4::any(), port_range.first);
m_listen_port_range = port_range; m_listen_port_retries = port_range.second - port_range.first;
// if the interface is the same and the socket is open // if the interface is the same and the socket is open
// don't do anything // don't do anything
if (new_interface == m_listen_interface if (new_interface == m_listen_interface
&& m_listen_socket) return true; && !m_listen_sockets.empty()) return true;
if (m_listen_socket)
m_listen_socket.reset();
m_incoming_connection = false;
m_listen_interface = new_interface; m_listen_interface = new_interface;
open_listen_port(); open_listen_port();
@ -1723,13 +1814,14 @@ namespace detail
(*m_logger) << time_now_string() << "\n"; (*m_logger) << time_now_string() << "\n";
#endif #endif
return m_listen_socket; return !m_listen_sockets.empty();
} }
unsigned short session_impl::listen_port() const unsigned short session_impl::listen_port() const
{ {
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
return m_external_listen_port; if (m_listen_sockets.empty()) return 0;
return m_listen_sockets.front().external_port;;
} }
void session_impl::announce_lsd(sha1_hash const& ih) void session_impl::announce_lsd(sha1_hash const& ih)
@ -1777,7 +1869,8 @@ namespace detail
if (tcp_port != 0) if (tcp_port != 0)
{ {
m_external_listen_port = tcp_port; if (!m_listen_sockets.empty())
m_listen_sockets.front().external_port = tcp_port;
if (m_alerts.should_post(alert::info)) if (m_alerts.should_post(alert::info))
{ {
std::stringstream msg; std::stringstream msg;
@ -1801,7 +1894,7 @@ namespace detail
{ {
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
INVARIANT_CHECK; // INVARIANT_CHECK;
session_status s; session_status s;
s.has_incoming_connections = m_incoming_connection; s.has_incoming_connections = m_incoming_connection;
@ -1945,7 +2038,7 @@ namespace detail
bool session_impl::is_listening() const bool session_impl::is_listening() const
{ {
mutex_t::scoped_lock l(m_mutex); mutex_t::scoped_lock l(m_mutex);
return m_listen_socket; return !m_listen_sockets.empty();
} }
session_impl::~session_impl() session_impl::~session_impl()

View File

@ -412,9 +412,9 @@ namespace libtorrent
m_last_dht_announce = now; m_last_dht_announce = now;
// TODO: There should be a way to abort an announce operation on the dht. // TODO: There should be a way to abort an announce operation on the dht.
// when the torrent is destructed // when the torrent is destructed
assert(m_ses.m_external_listen_port > 0); if (m_ses.m_listen_sockets.empty()) return;
m_ses.m_dht->announce(m_torrent_file->info_hash() m_ses.m_dht->announce(m_torrent_file->info_hash()
, m_ses.m_external_listen_port , m_ses.m_listen_sockets.front().external_port
, m_ses.m_strand.wrap(bind(&torrent::on_dht_announce_response_disp, self, _1))); , m_ses.m_strand.wrap(bind(&torrent::on_dht_announce_response_disp, self, _1)));
} }
#endif #endif
@ -1367,6 +1367,9 @@ namespace libtorrent
req.left = bytes_left(); req.left = bytes_left();
if (req.left == -1) req.left = 16*1024; if (req.left == -1) req.left = 16*1024;
req.event = m_event; req.event = m_event;
tcp::endpoint ep = m_ses.get_ipv6_interface();
if (ep != tcp::endpoint())
req.ipv6 = ep.address().to_string();
if (m_event != tracker_request::stopped) if (m_event != tracker_request::stopped)
m_event = tracker_request::none; m_event = tracker_request::none;

View File

@ -247,6 +247,18 @@ try
EXT: EXT:
Cache-Control:max-age=180 Cache-Control:max-age=180
DATE: Fri, 02 Jan 1970 08:10:38 GMT DATE: Fri, 02 Jan 1970 08:10:38 GMT
a notification looks like this:
NOTIFY * HTTP/1.1
Host:239.255.255.250:1900
NT:urn:schemas-upnp-org:device:MediaServer:1
NTS:ssdp:alive
Location:http://10.0.3.169:2869/upnphost/udhisapi.dll?content=uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e
USN:uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e::urn:schemas-upnp-org:device:MediaServer:1
Cache-Control:max-age=900
Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0
*/ */
http_parser p; http_parser p;
try try
@ -263,12 +275,17 @@ try
return; return;
} }
if (p.status_code() != 200) if (p.status_code() != 200 && p.method() != "notify")
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() if (p.method().empty())
<< " <== Rootdevice responded with HTTP status: " << p.status_code() m_log << time_now_string()
<< ". Ignoring device" << std::endl; << " <== Device responded with HTTP status: " << p.status_code()
<< ". Ignoring device" << std::endl;
else
m_log << time_now_string()
<< " <== Device with HTTP method: " << p.method()
<< ". Ignoring device" << std::endl;
#endif #endif
return; return;
} }