upnp and lsd update. added a broadcast_socket and made the upnp connection use the locally bound ip to specify its address in the soap requests

This commit is contained in:
Arvid Norberg 2007-09-09 23:52:34 +00:00
parent 4fcdea4172
commit 0d02fe0539
13 changed files with 513 additions and 292 deletions

View File

@ -235,6 +235,8 @@ SOURCES =
file_pool file_pool
lsd lsd
disk_io_thread disk_io_thread
enum_net
broadcast_socket
; ;
KADEMLIA_SOURCES = KADEMLIA_SOURCES =

View File

@ -0,0 +1,80 @@
/*
Copyright (c) 2007, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_BROADCAST_SOCKET_HPP_INCLUDED
#define TORRENT_BROADCAST_SOCKET_HPP_INCLUDED
#include "libtorrent/socket.hpp"
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <list>
namespace libtorrent
{
bool is_local(address const& a);
address_v4 guess_local_address(asio::io_service&);
typedef boost::function<void(udp::endpoint const& from
, char* buffer, int size)> receive_handler_t;
class broadcast_socket
{
public:
broadcast_socket(asio::io_service& ios, udp::endpoint const& multicast_endpoint
, receive_handler_t const& handler);
void send(char const* buffer, int size, asio::error_code& ec);
void close();
private:
struct socket_entry
{
socket_entry(boost::shared_ptr<datagram_socket> const& s): socket(s) {}
boost::shared_ptr<datagram_socket> socket;
char buffer[1024];
udp::endpoint remote;
};
void on_receive(socket_entry* s, asio::error_code const& ec
, std::size_t bytes_transferred);
std::list<socket_entry> m_sockets;
udp::endpoint m_multicast_endpoint;
receive_handler_t m_on_receive;
};
}
#endif

View File

@ -0,0 +1,44 @@
/*
Copyright (c) 2007, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_ENUM_NET_HPP_INCLUDED
#define TORRENT_ENUM_NET_HPP_INCLUDED
#include "libtorrent/socket.hpp"
namespace libtorrent
{
std::vector<address> const& enum_net_interfaces(asio::io_service& ios, asio::error_code& ec);
}
#endif

View File

@ -48,9 +48,13 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent namespace libtorrent
{ {
struct http_connection;
typedef boost::function<void(asio::error_code const& typedef boost::function<void(asio::error_code const&
, http_parser const&, char const* data, int size)> http_handler; , http_parser const&, char const* data, int size)> http_handler;
typedef boost::function<void(http_connection&)> http_connect_handler;
// TODO: add bind interface // TODO: add bind interface
// when bottled, the last two arguments to the handler // when bottled, the last two arguments to the handler
@ -58,11 +62,13 @@ typedef boost::function<void(asio::error_code const&
struct http_connection : boost::enable_shared_from_this<http_connection>, boost::noncopyable struct http_connection : boost::enable_shared_from_this<http_connection>, boost::noncopyable
{ {
http_connection(asio::io_service& ios, connection_queue& cc http_connection(asio::io_service& ios, connection_queue& cc
, http_handler handler, bool bottled = true) , http_handler const& handler, bool bottled = true
, http_connect_handler const& ch = http_connect_handler())
: m_sock(ios) : m_sock(ios)
, m_read_pos(0) , m_read_pos(0)
, m_resolver(ios) , m_resolver(ios)
, m_handler(handler) , m_handler(handler)
, m_connect_handler(ch)
, m_timer(ios) , m_timer(ios)
, m_last_receive(time_now()) , m_last_receive(time_now())
, m_bottled(bottled) , m_bottled(bottled)
@ -92,6 +98,8 @@ struct http_connection : boost::enable_shared_from_this<http_connection>, boost:
, time_duration timeout, bool handle_redirect = true); , time_duration timeout, bool handle_redirect = true);
void close(); void close();
tcp::socket const& socket() const { return m_sock; }
private: private:
void on_resolve(asio::error_code const& e void on_resolve(asio::error_code const& e
@ -112,6 +120,7 @@ private:
tcp::resolver m_resolver; tcp::resolver m_resolver;
http_parser m_parser; http_parser m_parser;
http_handler m_handler; http_handler m_handler;
http_connect_handler m_connect_handler;
deadline_timer m_timer; deadline_timer m_timer;
time_duration m_timeout; time_duration m_timeout;
ptime m_last_receive; ptime m_last_receive;

View File

@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/socket.hpp" #include "libtorrent/socket.hpp"
#include "libtorrent/peer_id.hpp" #include "libtorrent/peer_id.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include <boost/function.hpp> #include <boost/function.hpp>
#include <boost/noncopyable.hpp> #include <boost/noncopyable.hpp>
@ -58,35 +59,26 @@ public:
, peer_callback_t const& cb); , peer_callback_t const& cb);
~lsd(); ~lsd();
void rebind(address const& listen_interface); // void rebind(address const& listen_interface);
void announce(sha1_hash const& ih, int listen_port); void announce(sha1_hash const& ih, int listen_port);
void close(); void close();
private: private:
static address_v4 lsd_multicast_address;
static udp::endpoint lsd_multicast_endpoint;
void resend_announce(asio::error_code const& e, std::string msg); void resend_announce(asio::error_code const& e, std::string msg);
void on_announce(asio::error_code const& e void on_announce(udp::endpoint const& from, char* buffer
, std::size_t bytes_transferred); , std::size_t bytes_transferred);
void setup_receive(); // void setup_receive();
peer_callback_t m_callback; peer_callback_t m_callback;
// current retry count // current retry count
int m_retry_count; int m_retry_count;
// used to receive responses in
char m_receive_buffer[1024];
// the endpoint we received the message from
udp::endpoint m_remote;
// the udp socket used to send and receive // the udp socket used to send and receive
// multicast messages on // multicast messages on
datagram_socket m_socket; broadcast_socket m_socket;
// used to resend udp packets in case // used to resend udp packets in case
// they time out // they time out

View File

@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE.
#define TORRENT_UPNP_HPP #define TORRENT_UPNP_HPP
#include "libtorrent/socket.hpp" #include "libtorrent/socket.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include "libtorrent/http_connection.hpp" #include "libtorrent/http_connection.hpp"
#include "libtorrent/connection_queue.hpp" #include "libtorrent/connection_queue.hpp"
@ -56,9 +57,6 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent namespace libtorrent
{ {
bool is_local(address const& a);
address_v4 guess_local_address(asio::io_service&);
// int: external tcp port // int: external tcp port
// int: external udp port // int: external udp port
// std::string: error message // std::string: error message
@ -72,8 +70,6 @@ public:
, portmap_callback_t const& cb); , portmap_callback_t const& cb);
~upnp(); ~upnp();
void rebind(address const& listen_interface);
// maps the ports, if a port is set to 0 // maps the ports, if a port is set to 0
// it will not be mapped // it will not be mapped
void set_mappings(int tcp, int udp); void set_mappings(int tcp, int udp);
@ -90,7 +86,7 @@ private:
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);
void on_reply(asio::error_code const& e void on_reply(udp::endpoint const& from, char* buffer
, std::size_t bytes_transferred); , std::size_t bytes_transferred);
void discover_device(); void discover_device();
@ -106,12 +102,15 @@ private:
, int mapping); , int mapping);
void on_expire(asio::error_code const& e); void on_expire(asio::error_code const& e);
void post(rootdevice& d, std::stringstream const& s
, 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); void unmap_port(rootdevice& d, int i);
void disable(); void disable();
void delete_port_mapping(rootdevice& d, int i);
void create_port_mapping(http_connection& c, rootdevice& d, int i);
void post(upnp::rootdevice const& d, std::string const& soap
, std::string const& soap_action);
struct mapping_t struct mapping_t
{ {
mapping_t() mapping_t()
@ -198,18 +197,13 @@ private:
// current retry count // current retry count
int m_retry_count; int m_retry_count;
// used to receive responses in asio::io_service& m_io_service;
char m_receive_buffer[1024];
// the endpoint we received the message from asio::strand m_strand;
udp::endpoint m_remote;
// the local address we're listening on
address_v4 m_local_ip;
// the udp socket used to send and receive // the udp socket used to send and receive
// multicast messages on the network // multicast messages on the network
datagram_socket m_socket; broadcast_socket m_socket;
// used to resend udp packets in case // used to resend udp packets in case
// they time out // they time out
@ -218,8 +212,6 @@ private:
// timer used to refresh mappings // timer used to refresh mappings
deadline_timer m_refresh_timer; deadline_timer m_refresh_timer;
asio::strand m_strand;
bool m_disabled; bool m_disabled;
bool m_closing; bool m_closing;

142
src/broadcast_socket.cpp Normal file
View File

@ -0,0 +1,142 @@
/*
Copyright (c) 2007, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/socket.hpp"
#include "libtorrent/enum_net.hpp"
#include "libtorrent/broadcast_socket.hpp"
#include <asio/ip/host_name.hpp>
#include <asio/ip/multicast.hpp>
#include <boost/bind.hpp>
namespace libtorrent
{
bool is_local(address const& a)
{
if (a.is_v6()) return false;
address_v4 a4 = a.to_v4();
unsigned long ip = a4.to_ulong();
return ((ip & 0xff000000) == 0x0a000000
|| (ip & 0xfff00000) == 0xac100000
|| (ip & 0xffff0000) == 0xc0a80000);
}
address_v4 guess_local_address(asio::io_service& ios)
{
// make a best guess of the interface we're using and its IP
udp::resolver r(ios);
udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0"));
for (;i != udp::resolver_iterator(); ++i)
{
// ignore the loopback
if (i->endpoint().address() == address_v4((127 << 24) + 1)) continue;
// ignore addresses that are not on a local network
if (!is_local(i->endpoint().address())) continue;
// ignore non-IPv4 addresses
if (i->endpoint().address().is_v4()) break;
}
if (i == udp::resolver_iterator()) return address_v4::any();
return i->endpoint().address().to_v4();
}
broadcast_socket::broadcast_socket(asio::io_service& ios
, udp::endpoint const& multicast_endpoint
, receive_handler_t const& handler)
: m_multicast_endpoint(multicast_endpoint)
, m_on_receive(handler)
{
assert(m_multicast_endpoint.address().is_v4());
assert(m_multicast_endpoint.address().to_v4().is_multicast());
using namespace asio::ip::multicast;
asio::error_code ec;
std::vector<address> interfaces = enum_net_interfaces(ios, ec);
for (std::vector<address>::const_iterator i = interfaces.begin()
, end(interfaces.end()); i != end; ++i)
{
// only broadcast to IPv4 addresses that are not local
if (!i->is_v4() || !is_local(*i)) continue;
// ignore the loopback interface
if (i->to_v4() == address_v4((127 << 24) + 1)) continue;
boost::shared_ptr<datagram_socket> s(new datagram_socket(ios));
s->open(udp::v4(), ec);
if (ec) continue;
s->set_option(datagram_socket::reuse_address(true), ec);
if (ec) continue;
s->bind(udp::endpoint(*i, 0), ec);
if (ec) continue;
s->set_option(join_group(multicast_endpoint.address()), ec);
if (ec) continue;
s->set_option(outbound_interface(i->to_v4()), ec);
if (ec) continue;
s->set_option(hops(255));
m_sockets.push_back(socket_entry(s));
socket_entry& se = m_sockets.back();
s->async_receive_from(asio::buffer(se.buffer, sizeof(se.buffer))
, se.remote, bind(&broadcast_socket::on_receive, this, &se, _1, _2));
}
}
void broadcast_socket::send(char const* buffer, int size, asio::error_code& ec)
{
for (std::list<socket_entry>::iterator i = m_sockets.begin()
, end(m_sockets.end()); i != end; ++i)
{
asio::error_code e;
i->socket->send_to(asio::buffer(buffer, size), m_multicast_endpoint, 0, e);
// std::cerr << " sending on " << i->socket->local_endpoint().address().to_string() << std::endl;
if (e) ec = e;
}
}
void broadcast_socket::on_receive(socket_entry* s, asio::error_code const& ec
, std::size_t bytes_transferred)
{
if (ec || bytes_transferred == 0) return;
m_on_receive(s->remote, s->buffer, bytes_transferred);
s->socket->async_receive_from(asio::buffer(s->buffer, sizeof(s->buffer))
, s->remote, bind(&broadcast_socket::on_receive, this, s, _1, _2));
}
void broadcast_socket::close()
{
for (std::list<socket_entry>::iterator i = m_sockets.begin()
, end(m_sockets.end()); i != end; ++i)
{
i->socket->close();
}
}
}

133
src/enum_net.cpp Normal file
View File

@ -0,0 +1,133 @@
/*
Copyright (c) 2007, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#if defined __linux__ || defined __MACH__
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#endif
#include "libtorrent/enum_net.hpp"
namespace libtorrent
{
std::vector<address> const& enum_net_interfaces(asio::io_service& ios, asio::error_code& ec)
{
static std::vector<address> ret;
if (!ret.empty()) return ret;
#if defined __linux__ || defined __MACH__ || defined(__FreeBSD__)
int s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
{
ec = asio::error::fault;
return ret;
}
ifconf ifc;
char buf[1024];
ifc.ifc_len = sizeof(buf);
ifc.ifc_buf = buf;
if (ioctl(s, SIOCGIFCONF, &ifc) < 0)
{
close(s);
ec = asio::error::fault;
return ret;
}
close(s);
char *ifr = (char*)ifc.ifc_req;
int remaining = ifc.ifc_len;
while (remaining)
{
ifreq const& item = *reinterpret_cast<ifreq*>(ifr);
if (item.ifr_addr.sa_family == AF_INET)
{
ret.push_back(address::from_string(
inet_ntoa(((sockaddr_in const*)&item.ifr_addr)->sin_addr)));
}
#if defined __MACH__ || defined(__FreeBSD__)
int current_size = item.ifr_addr.sa_len + IFNAMSIZ;
#elif defined __linux__
int current_size = sizeof(ifreq);
#endif
ifr += current_size;
remaining -= current_size;
}
#elif defined WIN32
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);
if (s == SOCKET_ERROR)
{
ec = asio::error::fault;
return ret;
}
INTERFACE_INFO buffer[30];
DWORD size;
if (WSAIoctl(s, SIO_GET_INTERFACE_LIST, 0, 0, buffer,
sizeof(buffer), &size, 0, 0) != 0)
{
closesocket(s);
ec = asio::error::fault;
return ret;
}
closesocket(s);
int n = size / sizeof(INTERFACE_INFO);
for (int i = 0; i < n; ++i)
{
sockaddr_in *sockaddr = (sockaddr_in*)&buffer[i].iiAddress;
address a(address::from_string(inet_ntoa(sockaddr->sin_addr)));
if (a == address_v4::any()) continue;
ret.push_back(a);
}
#else
// make a best guess of the interface we're using and its IP
udp::resolver r(ios);
udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0"));
for (;i != udp::resolver_iterator(); ++i)
{
ret.push_back(i->endpoint().address());
}
#endif
return ret;
}
}

View File

@ -165,6 +165,7 @@ void http_connection::on_connect(asio::error_code const& e
if (!e) if (!e)
{ {
m_last_receive = time_now(); m_last_receive = time_now();
if (m_connect_handler) m_connect_handler(*this);
asio::async_write(m_sock, asio::buffer(sendbuffer) asio::async_write(m_sock, asio::buffer(sendbuffer)
, bind(&http_connection::on_write, shared_from_this(), _1)); , bind(&http_connection::on_write, shared_from_this(), _1));
} }

View File

@ -35,6 +35,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/lsd.hpp" #include "libtorrent/lsd.hpp"
#include "libtorrent/io.hpp" #include "libtorrent/io.hpp"
#include "libtorrent/http_tracker_connection.hpp" #include "libtorrent/http_tracker_connection.hpp"
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/ref.hpp> #include <boost/ref.hpp>
#include <asio/ip/host_name.hpp> #include <asio/ip/host_name.hpp>
@ -52,76 +53,22 @@ namespace libtorrent
address_v4 guess_local_address(asio::io_service&); address_v4 guess_local_address(asio::io_service&);
} }
address_v4 lsd::lsd_multicast_address;
udp::endpoint lsd::lsd_multicast_endpoint;
lsd::lsd(io_service& ios, address const& listen_interface lsd::lsd(io_service& ios, address const& listen_interface
, peer_callback_t const& cb) , peer_callback_t const& cb)
: m_callback(cb) : m_callback(cb)
, m_retry_count(0) , m_retry_count(0)
, m_socket(ios) , m_socket(ios, udp::endpoint(address_v4::from_string("239.192.152.143"), 6771)
, bind(&lsd::on_announce, this, _1, _2, _3))
, m_broadcast_timer(ios) , m_broadcast_timer(ios)
, m_disabled(false) , m_disabled(false)
{ {
// Bittorrent Local discovery multicast address and port
lsd_multicast_address = address_v4::from_string("239.192.152.143");
lsd_multicast_endpoint = udp::endpoint(lsd_multicast_address, 6771);
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc); m_log.open("lsd.log", std::ios::in | std::ios::out | std::ios::trunc);
#endif #endif
assert(lsd_multicast_address.is_multicast());
rebind(listen_interface);
} }
lsd::~lsd() {} lsd::~lsd() {}
void lsd::rebind(address const& listen_interface)
{
address_v4 local_ip = address_v4::any();
if (listen_interface.is_v4() && listen_interface != address_v4::any())
{
local_ip = listen_interface.to_v4();
}
try
{
// the local interface hasn't changed
if (m_socket.is_open()
&& m_socket.local_endpoint().address() == local_ip)
return;
m_socket.close();
using namespace asio::ip::multicast;
m_socket.open(udp::v4());
m_socket.set_option(datagram_socket::reuse_address(true));
m_socket.bind(udp::endpoint(local_ip, 6771));
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << "local ip: " << local_ip << std::endl;
#endif
m_socket.set_option(join_group(lsd_multicast_address));
m_socket.set_option(outbound_interface(local_ip));
m_socket.set_option(enable_loopback(true));
m_socket.set_option(hops(255));
}
catch (std::exception& e)
{
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << "socket multicast error " << e.what()
<< ". disabling local service discovery" << std::endl;
#endif
m_disabled = true;
return;
}
m_disabled = false;
setup_receive();
}
void lsd::announce(sha1_hash const& ih, int listen_port) void lsd::announce(sha1_hash const& ih, int listen_port)
{ {
if (m_disabled) return; if (m_disabled) return;
@ -136,8 +83,7 @@ void lsd::announce(sha1_hash const& ih, int listen_port)
m_retry_count = 0; m_retry_count = 0;
asio::error_code ec; asio::error_code ec;
m_socket.send_to(asio::buffer(msg.c_str(), msg.size() - 1) m_socket.send(msg.c_str(), int(msg.size()), ec);
, lsd_multicast_endpoint, 0, ec);
if (ec) if (ec)
{ {
m_disabled = true; m_disabled = true;
@ -157,8 +103,8 @@ void lsd::resend_announce(asio::error_code const& e, std::string msg) try
{ {
if (e) return; if (e) return;
m_socket.send_to(asio::buffer(msg, msg.size() - 1) asio::error_code ec;
, lsd_multicast_endpoint); m_socket.send(msg.c_str(), int(msg.size()), ec);
++m_retry_count; ++m_retry_count;
if (m_retry_count >= 5) if (m_retry_count >= 5)
@ -170,14 +116,13 @@ void lsd::resend_announce(asio::error_code const& e, std::string msg) try
catch (std::exception&) catch (std::exception&)
{} {}
void lsd::on_announce(asio::error_code const& e void lsd::on_announce(udp::endpoint const& from, char* buffer
, std::size_t bytes_transferred) , std::size_t bytes_transferred)
{ {
using namespace libtorrent::detail; using namespace libtorrent::detail;
if (e) return;
char* p = m_receive_buffer; char* p = buffer;
char* end = m_receive_buffer + bytes_transferred; char* end = buffer + bytes_transferred;
char* line = std::find(p, end, '\n'); char* line = std::find(p, end, '\n');
for (char* i = p; i < line; ++i) *i = std::tolower(*i); for (char* i = p; i < line; ++i) *i = std::tolower(*i);
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
@ -190,7 +135,6 @@ void lsd::on_announce(asio::error_code const& e
m_log << time_now_string() m_log << time_now_string()
<< " *** assumed 'bt-search', ignoring" << std::endl; << " *** assumed 'bt-search', ignoring" << std::endl;
#endif #endif
setup_receive();
return; return;
} }
p = line + 1; p = line + 1;
@ -223,25 +167,15 @@ void lsd::on_announce(asio::error_code const& e
{ {
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING) #if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
m_log << time_now_string() m_log << time_now_string()
<< " *** incoming local announce " << m_remote.address() << " *** incoming local announce " << from.address()
<< ":" << port << " ih: " << ih << std::endl; << ":" << port << " ih: " << ih << std::endl;
#endif #endif
// we got an announce, pass it on through the callback // we got an announce, pass it on through the callback
try { m_callback(tcp::endpoint(m_remote.address(), port), ih); } try { m_callback(tcp::endpoint(from.address(), port), ih); }
catch (std::exception&) {} catch (std::exception&) {}
} }
setup_receive();
} }
void lsd::setup_receive() try
{
assert(m_socket.is_open());
m_socket.async_receive_from(asio::buffer(m_receive_buffer
, sizeof(m_receive_buffer)), m_remote, bind(&lsd::on_announce, this, _1, _2));
}
catch (std::exception&)
{}
void lsd::close() void lsd::close()
{ {
m_socket.close(); m_socket.close();

View File

@ -1709,16 +1709,6 @@ namespace detail
bool new_listen_address = m_listen_interface.address() != new_interface.address(); bool new_listen_address = m_listen_interface.address() != new_interface.address();
if (new_listen_address)
{
if (m_natpmp.get())
m_natpmp->rebind(new_interface.address());
if (m_upnp.get())
m_upnp->rebind(new_interface.address());
if (m_lsd.get())
m_lsd->rebind(new_interface.address());
}
if (m_natpmp.get()) if (m_natpmp.get())
m_natpmp->set_mappings(m_listen_interface.port(), 0); m_natpmp->set_mappings(m_listen_interface.port(), 0);
if (m_upnp.get()) if (m_upnp.get())

View File

@ -53,38 +53,10 @@ POSSIBILITY OF SUCH DAMAGE.
using boost::bind; using boost::bind;
using namespace libtorrent; using namespace libtorrent;
address_v4 upnp::upnp_multicast_address;
udp::endpoint upnp::upnp_multicast_endpoint;
namespace libtorrent namespace libtorrent
{ {
bool is_local(address const& a) bool is_local(address const& a);
{ address_v4 guess_local_address(asio::io_service&);
if (a.is_v6()) return false;
address_v4 a4 = a.to_v4();
unsigned long ip = a4.to_ulong();
return ((ip & 0xff000000) == 0x0a000000
|| (ip & 0xfff00000) == 0xac100000
|| (ip & 0xffff0000) == 0xc0a80000);
}
address_v4 guess_local_address(asio::io_service& ios)
{
// make a best guess of the interface we're using and its IP
udp::resolver r(ios);
udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0"));
for (;i != udp::resolver_iterator(); ++i)
{
// ignore the loopback
if (i->endpoint().address() == address_v4((127 << 24) + 1)) continue;
// ignore addresses that are not on a local network
if (!is_local(i->endpoint().address())) continue;
// ignore non-IPv4 addresses
if (i->endpoint().address().is_v4()) break;
}
if (i == udp::resolver_iterator()) return address_v4::any();
return i->endpoint().address().to_v4();
}
} }
upnp::upnp(io_service& ios, connection_queue& cc upnp::upnp(io_service& ios, connection_queue& cc
@ -95,89 +67,27 @@ upnp::upnp(io_service& ios, connection_queue& cc
, m_user_agent(user_agent) , m_user_agent(user_agent)
, m_callback(cb) , m_callback(cb)
, m_retry_count(0) , m_retry_count(0)
, m_socket(ios) , m_io_service(ios)
, m_strand(ios)
, m_socket(ios, udp::endpoint(address_v4::from_string("239.255.255.250"), 1900)
, m_strand.wrap(bind(&upnp::on_reply, this, _1, _2, _3)))
, m_broadcast_timer(ios) , m_broadcast_timer(ios)
, m_refresh_timer(ios) , m_refresh_timer(ios)
, m_strand(ios)
, m_disabled(false) , m_disabled(false)
, m_closing(false) , m_closing(false)
, m_cc(cc) , m_cc(cc)
{ {
// UPnP multicast address and port
upnp_multicast_address = address_v4::from_string("239.255.255.250");
upnp_multicast_endpoint = udp::endpoint(upnp_multicast_address, 1900);
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_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);
#endif #endif
rebind(listen_interface); m_retry_count = 0;
discover_device();
} }
upnp::~upnp() upnp::~upnp()
{ {
} }
void upnp::rebind(address const& listen_interface) try
{
address_v4 bind_to = address_v4::any();
if (listen_interface.is_v4() && listen_interface != address_v4::any())
{
m_local_ip = listen_interface.to_v4();
bind_to = listen_interface.to_v4();
if (!is_local(m_local_ip))
{
// the local address seems to be an external
// internet address. Assume it is not behind a NAT
throw std::runtime_error("local IP is not on a local network");
}
}
else
{
m_local_ip = guess_local_address(m_socket.io_service());
bind_to = address_v4::any();
}
if (!is_local(m_local_ip))
{
throw std::runtime_error("local host is probably not on a NATed "
"network. disabling UPnP");
}
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " local ip: " << m_local_ip.to_string()
<< " bind to: " << bind_to.to_string() << std::endl;
#endif
// the local interface hasn't changed
if (m_socket.is_open()
&& m_socket.local_endpoint().address() == m_local_ip)
return;
m_socket.close();
using namespace asio::ip::multicast;
m_socket.open(udp::v4());
m_socket.set_option(datagram_socket::reuse_address(true));
m_socket.bind(udp::endpoint(bind_to, 0));
m_socket.set_option(join_group(upnp_multicast_address));
m_socket.set_option(outbound_interface(bind_to));
m_socket.set_option(hops(255));
m_disabled = false;
m_retry_count = 0;
discover_device();
}
catch (std::exception& e)
{
disable();
std::stringstream msg;
msg << "UPnP portmapping disabled: " << e.what();
m_callback(0, 0, msg.str());
};
void upnp::discover_device() try void upnp::discover_device() try
{ {
const char msearch[] = const char msearch[] =
@ -188,20 +98,20 @@ void upnp::discover_device() try
"MX:3\r\n" "MX:3\r\n"
"\r\n\r\n"; "\r\n\r\n";
m_socket.async_receive_from(asio::buffer(m_receive_buffer
, sizeof(m_receive_buffer)), m_remote, m_strand.wrap(bind(
&upnp::on_reply, this, _1, _2)));
asio::error_code ec; asio::error_code ec;
#ifdef TORRENT_DEBUG_UPNP #ifdef TORRENT_DEBUG_UPNP
// simulate packet loss // simulate packet loss
if (m_retry_count & 1) if (m_retry_count & 1)
#endif #endif
m_socket.send_to(asio::buffer(msearch, sizeof(msearch) - 1) m_socket.send(msearch, sizeof(msearch) - 1, ec);
, upnp_multicast_endpoint, 0, ec);
if (ec) if (ec)
{ {
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> Broadcast FAILED: " << ec.message() << std::endl
<< "aborting" << std::endl;
#endif
disable(); disable();
return; return;
} }
@ -292,7 +202,7 @@ try
rootdevice& d = const_cast<rootdevice&>(*i); rootdevice& d = const_cast<rootdevice&>(*i);
try try
{ {
d.upnp_connection.reset(new http_connection(m_socket.io_service() d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, this, _1, _2 , m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, this, _1, _2
, boost::ref(d))))); , boost::ref(d)))));
d.upnp_connection->get(d.url); d.upnp_connection->get(d.url);
@ -316,20 +226,13 @@ catch (std::exception&)
} }
#endif #endif
void upnp::on_reply(asio::error_code const& e void upnp::on_reply(udp::endpoint const& from, char* buffer
, std::size_t bytes_transferred) , std::size_t bytes_transferred)
#ifndef NDEBUG #ifndef NDEBUG
try try
#endif #endif
{ {
using namespace libtorrent::detail; using namespace libtorrent::detail;
if (e)
{
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() << " *** on_reply aborted: " << e.message() << std::endl;
#endif
return;
}
// parse out the url for the device // parse out the url for the device
@ -348,15 +251,14 @@ try
http_parser p; http_parser p;
try try
{ {
p.incoming(buffer::const_interval(m_receive_buffer p.incoming(buffer::const_interval(buffer
, m_receive_buffer + bytes_transferred)); , buffer + bytes_transferred));
} }
catch (std::exception& e) catch (std::exception& e)
{ {
#ifdef TORRENT_UPNP_LOGGING #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string() m_log << time_now_string()
<< " <== Rootdevice responded with incorrect HTTP packet: " << " <== Rootdevice responded with incorrect HTTP packet. Ignoring device" << std::endl;
<< e.what() << ". Ignoring device" << std::endl;
#endif #endif
return; return;
} }
@ -469,7 +371,7 @@ try
rootdevice& d = const_cast<rootdevice&>(*i); rootdevice& d = const_cast<rootdevice&>(*i);
try try
{ {
d.upnp_connection.reset(new http_connection(m_socket.io_service() d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, this, _1, _2 , m_cc, m_strand.wrap(bind(&upnp::on_upnp_xml, this, _1, _2
, boost::ref(d))))); , boost::ref(d)))));
d.upnp_connection->get(d.url); d.upnp_connection->get(d.url);
@ -494,7 +396,7 @@ catch (std::exception&)
}; };
#endif #endif
void upnp::post(rootdevice& d, std::stringstream const& soap void upnp::post(upnp::rootdevice const& d, std::string const& soap
, std::string const& soap_action) , std::string const& soap_action)
{ {
std::stringstream header; std::stringstream header;
@ -502,12 +404,40 @@ void upnp::post(rootdevice& d, std::stringstream const& soap
header << "POST " << d.control_url << " HTTP/1.1\r\n" header << "POST " << d.control_url << " HTTP/1.1\r\n"
"Host: " << d.hostname << ":" << d.port << "\r\n" "Host: " << d.hostname << ":" << d.port << "\r\n"
"Content-Type: text/xml; charset=\"utf-8\"\r\n" "Content-Type: text/xml; charset=\"utf-8\"\r\n"
"Content-Length: " << soap.str().size() << "\r\n" "Content-Length: " << soap.size() << "\r\n"
"Soapaction: \"" << d.service_namespace << "#" << soap_action << "\"\r\n\r\n" << soap.str(); "Soapaction: \"" << d.service_namespace << "#" << soap_action << "\"\r\n\r\n" << soap;
d.upnp_connection->sendbuffer = header.str(); d.upnp_connection->sendbuffer = header.str();
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
, seconds(10)); #ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> sending: " << soap << std::endl;
#endif
}
void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
{
std::string soap_action = "AddPortMapping";
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>"
"<NewInternalPort>" << d.mapping[i].local_port << "</NewInternalPort>"
"<NewInternalClient>" << c.socket().local_endpoint().address().to_string() << "</NewInternalClient>"
"<NewEnabled>1</NewEnabled>"
"<NewPortMappingDescription>" << m_user_agent << "</NewPortMappingDescription>"
"<NewLeaseDuration>" << d.lease_duration << "</NewLeaseDuration>";
soap << "</u:" << soap_action << "></s:Body></s:Envelope>";
post(d, soap.str(), soap_action);
} }
void upnp::map_port(rootdevice& d, int i) void upnp::map_port(rootdevice& d, int i)
@ -528,14 +458,21 @@ void upnp::map_port(rootdevice& d, int i)
assert(!d.upnp_connection); assert(!d.upnp_connection);
assert(d.service_namespace); assert(d.service_namespace);
d.upnp_connection.reset(new http_connection(m_socket.io_service() d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_map_response, this, _1, _2 , m_cc, m_strand.wrap(bind(&upnp::on_upnp_map_response, this, _1, _2
, boost::ref(d), i)))); , boost::ref(d), i)), true
, bind(&upnp::create_port_mapping, this, _1, boost::ref(d), i)));
std::string soap_action = "AddPortMapping"; d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
, seconds(10));
}
void upnp::delete_port_mapping(rootdevice& d, int i)
{
std::stringstream soap; std::stringstream soap;
std::string soap_action = "DeletePortMapping";
soap << "<?xml version=\"1.0\"?>\n" soap << "<?xml version=\"1.0\"?>\n"
"<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/\">"
@ -543,20 +480,10 @@ void upnp::map_port(rootdevice& d, int i)
soap << "<NewRemoteHost></NewRemoteHost>" soap << "<NewRemoteHost></NewRemoteHost>"
"<NewExternalPort>" << d.mapping[i].external_port << "</NewExternalPort>" "<NewExternalPort>" << d.mapping[i].external_port << "</NewExternalPort>"
"<NewProtocol>" << (d.mapping[i].protocol ? "UDP" : "TCP") << "</NewProtocol>" "<NewProtocol>" << (d.mapping[i].protocol ? "UDP" : "TCP") << "</NewProtocol>";
"<NewInternalPort>" << d.mapping[i].local_port << "</NewInternalPort>"
"<NewInternalClient>" << m_local_ip.to_string() << "</NewInternalClient>"
"<NewEnabled>1</NewEnabled>"
"<NewPortMappingDescription>" << m_user_agent << "</NewPortMappingDescription>"
"<NewLeaseDuration>" << d.lease_duration << "</NewLeaseDuration>";
soap << "</u:" << soap_action << "></s:Body></s:Envelope>"; soap << "</u:" << soap_action << "></s:Body></s:Envelope>";
post(d, soap, soap_action); post(d, soap.str(), soap_action);
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> AddPortMapping: " << soap.str() << std::endl;
#endif
} }
// requires the mutex to be locked // requires the mutex to be locked
@ -574,29 +501,13 @@ void upnp::unmap_port(rootdevice& d, int i)
} }
return; return;
} }
d.upnp_connection.reset(new http_connection(m_socket.io_service() d.upnp_connection.reset(new http_connection(m_io_service
, m_cc, m_strand.wrap(bind(&upnp::on_upnp_unmap_response, this, _1, _2 , m_cc, m_strand.wrap(bind(&upnp::on_upnp_unmap_response, this, _1, _2
, boost::ref(d), i)))); , boost::ref(d), i)), true
, bind(&upnp::delete_port_mapping, this, boost::ref(d), i)));
std::string soap_action = "DeletePortMapping"; d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
, seconds(10));
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);
#ifdef TORRENT_UPNP_LOGGING
m_log << time_now_string()
<< " ==> DeletePortMapping: " << soap.str() << std::endl;
#endif
} }
namespace namespace

View File

@ -16,9 +16,9 @@ int main(int argc, char* argv[])
io_service ios; io_service ios;
std::string user_agent = "test agent"; std::string user_agent = "test agent";
if (argc != 4) if (argc != 3)
{ {
std::cerr << "usage: " << argv[0] << " bind-address tcp-port udp-port" << std::endl; std::cerr << "usage: " << argv[0] << " tcp-port udp-port" << std::endl;
return 1; return 1;
} }
@ -34,20 +34,11 @@ int main(int argc, char* argv[])
ios.reset(); ios.reset();
ios.run(); ios.run();
upnp_handler.rebind(address_v4::from_string(argv[1])); upnp_handler.set_mappings(atoi(argv[1]), atoi(argv[2]));
timer.expires_from_now(seconds(2));
timer.async_wait(boost::bind(&io_service::stop, boost::ref(ios)));
std::cerr << "rebinding to IP " << argv[1]
<< " broadcasting for UPnP device" << std::endl;
ios.reset();
ios.run();
upnp_handler.set_mappings(atoi(argv[2]), atoi(argv[3]));
timer.expires_from_now(seconds(5)); timer.expires_from_now(seconds(5));
timer.async_wait(boost::bind(&io_service::stop, boost::ref(ios))); timer.async_wait(boost::bind(&io_service::stop, boost::ref(ios)));
std::cerr << "mapping ports TCP: " << argv[2] std::cerr << "mapping ports TCP: " << argv[1]
<< " UDP: " << argv[3] << std::endl; << " UDP: " << argv[2] << std::endl;
ios.reset(); ios.reset();
ios.run(); ios.run();