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:
parent
4fcdea4172
commit
0d02fe0539
2
Jamfile
2
Jamfile
|
@ -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 =
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
88
src/lsd.cpp
88
src/lsd.cpp
|
@ -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();
|
||||||
|
|
|
@ -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())
|
||||||
|
|
229
src/upnp.cpp
229
src/upnp.cpp
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue