added http_connection and a simple xml parser, used by a upnp implementation that still isn't finished
This commit is contained in:
parent
985e457e91
commit
29e43d7f62
2
Jamfile
2
Jamfile
|
@ -37,6 +37,7 @@ SOURCES =
|
||||||
entry.cpp
|
entry.cpp
|
||||||
escape_string.cpp
|
escape_string.cpp
|
||||||
file.cpp
|
file.cpp
|
||||||
|
http_connection.cpp
|
||||||
identify_client.cpp
|
identify_client.cpp
|
||||||
ip_filter.cpp
|
ip_filter.cpp
|
||||||
peer_connection.cpp
|
peer_connection.cpp
|
||||||
|
@ -57,6 +58,7 @@ SOURCES =
|
||||||
udp_tracker_connection.cpp
|
udp_tracker_connection.cpp
|
||||||
sha1.cpp
|
sha1.cpp
|
||||||
metadata_transfer.cpp
|
metadata_transfer.cpp
|
||||||
|
upnp.cpp
|
||||||
ut_pex.cpp
|
ut_pex.cpp
|
||||||
logger.cpp
|
logger.cpp
|
||||||
file_pool.cpp
|
file_pool.cpp
|
||||||
|
|
|
@ -13,6 +13,7 @@ libtorrent/file.hpp \
|
||||||
libtorrent/file_pool.hpp \
|
libtorrent/file_pool.hpp \
|
||||||
libtorrent/fingerprint.hpp \
|
libtorrent/fingerprint.hpp \
|
||||||
libtorrent/hasher.hpp \
|
libtorrent/hasher.hpp \
|
||||||
|
libtorrent/http_connection.hpp \
|
||||||
libtorrent/http_tracker_connection.hpp \
|
libtorrent/http_tracker_connection.hpp \
|
||||||
libtorrent/identify_client.hpp \
|
libtorrent/identify_client.hpp \
|
||||||
libtorrent/invariant_check.hpp \
|
libtorrent/invariant_check.hpp \
|
||||||
|
@ -45,6 +46,7 @@ libtorrent/torrent_info.hpp \
|
||||||
libtorrent/tracker_manager.hpp \
|
libtorrent/tracker_manager.hpp \
|
||||||
libtorrent/udp_tracker_connection.hpp \
|
libtorrent/udp_tracker_connection.hpp \
|
||||||
libtorrent/utf8.hpp \
|
libtorrent/utf8.hpp \
|
||||||
|
libtorrent/xml_parse.hpp \
|
||||||
libtorrent/version.hpp \
|
libtorrent/version.hpp \
|
||||||
libtorrent/aux_/allocate_resources_impl.hpp \
|
libtorrent/aux_/allocate_resources_impl.hpp \
|
||||||
libtorrent/aux_/session_impl.hpp \
|
libtorrent/aux_/session_impl.hpp \
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
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_HTTP_CONNECTION
|
||||||
|
#define TORRENT_HTTP_CONNECTION
|
||||||
|
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
#include <boost/enable_shared_from_this.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/date_time/time_duration.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "libtorrent/socket.hpp"
|
||||||
|
#include "libtorrent/http_tracker_connection.hpp"
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef boost::function<void(asio::error_code const&
|
||||||
|
, http_parser const&, char const* data, int size)> http_handler;
|
||||||
|
|
||||||
|
// TODO: add bind interface
|
||||||
|
|
||||||
|
// when bottled, the last two arguments to the handler
|
||||||
|
// will always be 0
|
||||||
|
struct http_connection : boost::enable_shared_from_this<http_connection>, boost::noncopyable
|
||||||
|
{
|
||||||
|
http_connection(asio::io_service& ios, http_handler handler, bool bottled = true)
|
||||||
|
: m_sock(ios)
|
||||||
|
, m_read_pos(0)
|
||||||
|
, m_resolver(ios)
|
||||||
|
, m_handler(handler)
|
||||||
|
, m_timer(ios)
|
||||||
|
, m_bottled(bottled)
|
||||||
|
{
|
||||||
|
assert(!m_handler.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string sendbuffer;
|
||||||
|
|
||||||
|
void get(std::string const& url, boost::posix_time::time_duration timeout
|
||||||
|
= boost::posix_time::seconds(30));
|
||||||
|
|
||||||
|
void start(std::string const& hostname, std::string const& port
|
||||||
|
, boost::posix_time::time_duration timeout);
|
||||||
|
void close();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void on_resolve(asio::error_code const& e
|
||||||
|
, tcp::resolver::iterator i);
|
||||||
|
void on_connect(asio::error_code const& e
|
||||||
|
/* , tcp::resolver::iterator i*/);
|
||||||
|
void on_write(asio::error_code const& e);
|
||||||
|
void on_read(asio::error_code const& e, std::size_t bytes_transferred);
|
||||||
|
void on_timeout(asio::error_code const& e);
|
||||||
|
|
||||||
|
std::vector<char> m_recvbuffer;
|
||||||
|
tcp::socket m_sock;
|
||||||
|
int m_read_pos;
|
||||||
|
tcp::resolver m_resolver;
|
||||||
|
http_parser m_parser;
|
||||||
|
http_handler m_handler;
|
||||||
|
deadline_timer m_timer;
|
||||||
|
boost::posix_time::time_duration m_timeout;
|
||||||
|
// bottled means that the handler is called once, when
|
||||||
|
// everything is received (and buffered in memory).
|
||||||
|
// non bottled means that once the headers have been
|
||||||
|
// received, data is streamed to the handler
|
||||||
|
bool m_bottled;
|
||||||
|
std::string m_hostname;
|
||||||
|
std::string m_port;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,201 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
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_UPNP_HPP
|
||||||
|
#define TORRENT_UPNP_HPP
|
||||||
|
|
||||||
|
#include "libtorrent/socket.hpp"
|
||||||
|
#include "libtorrent/http_connection.hpp"
|
||||||
|
#include <boost/date_time/posix_time/ptime.hpp>
|
||||||
|
#include <boost/function.hpp>
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
#include <fstream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
|
||||||
|
// int: external tcp port
|
||||||
|
// int: external udp port
|
||||||
|
// std::string: error message
|
||||||
|
typedef boost::function<void(int, int, std::string const&)> portmap_callback_t;
|
||||||
|
|
||||||
|
class upnp : boost::noncopyable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
upnp(io_service& ios, address const& listen_interface
|
||||||
|
, std::string const& user_agent, portmap_callback_t const& cb);
|
||||||
|
|
||||||
|
void rebind(address const& listen_interface);
|
||||||
|
|
||||||
|
// maps the ports, if a port is set to 0
|
||||||
|
// it will not be mapped
|
||||||
|
void set_mappings(int tcp, int udp);
|
||||||
|
|
||||||
|
void close();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void update_mapping(int i, int port);
|
||||||
|
void resend_request(asio::error_code const& e);
|
||||||
|
void on_reply(asio::error_code const& e
|
||||||
|
, std::size_t bytes_transferred);
|
||||||
|
void discover_device();
|
||||||
|
|
||||||
|
struct rootdevice;
|
||||||
|
|
||||||
|
void on_upnp_xml(asio::error_code const& e
|
||||||
|
, libtorrent::http_parser const& p, rootdevice& d);
|
||||||
|
void on_upnp_map_response(asio::error_code const& e
|
||||||
|
, libtorrent::http_parser const& p, rootdevice& d);
|
||||||
|
|
||||||
|
/*
|
||||||
|
void send_map_request(int i);
|
||||||
|
void try_next_mapping(int i);
|
||||||
|
void update_expiration_timer();
|
||||||
|
void refresh_mapping(int i);
|
||||||
|
void mapping_expired(asio::error_code const& e, int i);
|
||||||
|
*/
|
||||||
|
|
||||||
|
void post(rootdevice& d, std::stringstream const& s
|
||||||
|
, std::string const& soap_action);
|
||||||
|
void map_port(rootdevice& d, int i);
|
||||||
|
|
||||||
|
struct mapping_t
|
||||||
|
{
|
||||||
|
mapping_t()
|
||||||
|
: need_update(false)
|
||||||
|
, local_port(0)
|
||||||
|
, external_port(0)
|
||||||
|
, protocol(1)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// indicates that the mapping has changed
|
||||||
|
// and needs an update
|
||||||
|
bool need_update;
|
||||||
|
|
||||||
|
// the time the port mapping will expire
|
||||||
|
boost::posix_time::ptime expires;
|
||||||
|
|
||||||
|
// the local port for this mapping. If this is set
|
||||||
|
// to 0, the mapping is not in use
|
||||||
|
int local_port;
|
||||||
|
|
||||||
|
// the external (on the NAT router) port
|
||||||
|
// for the mapping. This is the port we
|
||||||
|
// should announce to others
|
||||||
|
int external_port;
|
||||||
|
|
||||||
|
// 1 = udp, 0 = tcp
|
||||||
|
int protocol;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct rootdevice
|
||||||
|
{
|
||||||
|
rootdevice(): ports_mapped(false), lease_duration(3600)
|
||||||
|
, supports_specific_external(true) {}
|
||||||
|
// the interface url, through which the list of
|
||||||
|
// supported interfaces are fetched
|
||||||
|
std::string url;
|
||||||
|
|
||||||
|
// the url to the WANIP or WANPPP interface
|
||||||
|
std::string control_url;
|
||||||
|
// either the WANIP namespace or the WANPPP namespace
|
||||||
|
std::string service_namespace;
|
||||||
|
|
||||||
|
mapping_t mapping[2];
|
||||||
|
|
||||||
|
// true if we already mapped the ports on this device
|
||||||
|
bool ports_mapped;
|
||||||
|
|
||||||
|
std::string hostname;
|
||||||
|
int port;
|
||||||
|
std::string path;
|
||||||
|
|
||||||
|
int lease_duration;
|
||||||
|
// true if the device supports specifying a
|
||||||
|
// specific external port, false if it doesn't
|
||||||
|
bool supports_specific_external;
|
||||||
|
|
||||||
|
boost::shared_ptr<http_connection> upnp_connection;
|
||||||
|
|
||||||
|
void close() const { if (upnp_connection) upnp_connection->close(); }
|
||||||
|
|
||||||
|
bool operator<(rootdevice const& rhs) const
|
||||||
|
{ return url < rhs.url; }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string const& m_user_agent;
|
||||||
|
|
||||||
|
// the set of devices we've found
|
||||||
|
std::set<rootdevice> m_devices;
|
||||||
|
|
||||||
|
portmap_callback_t m_callback;
|
||||||
|
|
||||||
|
// current 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 local address we're listening on
|
||||||
|
address_v4 m_local_ip;
|
||||||
|
|
||||||
|
// the udp socket used to send and receive
|
||||||
|
// multicast messages on the network
|
||||||
|
datagram_socket m_socket;
|
||||||
|
|
||||||
|
// used to resend udp packets in case
|
||||||
|
// they time out
|
||||||
|
deadline_timer m_broadcast_timer;
|
||||||
|
|
||||||
|
// timer used to refresh mappings
|
||||||
|
deadline_timer m_refresh_timer;
|
||||||
|
|
||||||
|
bool m_disabled;
|
||||||
|
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
std::ofstream m_log;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
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_XML_PARSE_HPP
|
||||||
|
#define TORRENT_XML_PARSE_HPP
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
const int xml_start_tag = 0;
|
||||||
|
const int xml_end_tag = 1;
|
||||||
|
const int xml_string = 2;
|
||||||
|
|
||||||
|
template <class CallbackType>
|
||||||
|
void xml_parse(char* p, char* end, CallbackType callback)
|
||||||
|
{
|
||||||
|
for(;p != end; ++p)
|
||||||
|
{
|
||||||
|
char const* start = p;
|
||||||
|
// look for tag start
|
||||||
|
for(; *p != '<' && p != end; ++p);
|
||||||
|
|
||||||
|
if (p != start)
|
||||||
|
{
|
||||||
|
if (p != end)
|
||||||
|
{
|
||||||
|
assert(*p == '<');
|
||||||
|
*p = 0;
|
||||||
|
}
|
||||||
|
callback(xml_string, start);
|
||||||
|
if (p != end) *p = '<';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p == end) break;
|
||||||
|
|
||||||
|
// skip '<'
|
||||||
|
++p;
|
||||||
|
|
||||||
|
// parse the name of the tag. Ignore attributes
|
||||||
|
for (start = p; p != end && *p != '>'; ++p)
|
||||||
|
{
|
||||||
|
// terminate the string at the first space
|
||||||
|
// to ignore tag attributes
|
||||||
|
if (*p == ' ') *p = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse error
|
||||||
|
if (p == end) break;
|
||||||
|
|
||||||
|
assert(*p == '>');
|
||||||
|
*p = 0;
|
||||||
|
|
||||||
|
if (*start == '/')
|
||||||
|
{
|
||||||
|
++start;
|
||||||
|
callback(xml_end_tag, start);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callback(xml_start_tag, start);
|
||||||
|
}
|
||||||
|
*p = '>';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -5,7 +5,7 @@ bandwidth_manager.cpp entry.cpp escape_string.cpp \
|
||||||
peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \
|
peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \
|
||||||
natpmp.cpp piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp \
|
natpmp.cpp piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp \
|
||||||
stat.cpp storage.cpp torrent.cpp torrent_handle.cpp \
|
stat.cpp storage.cpp torrent.cpp torrent_handle.cpp \
|
||||||
torrent_info.cpp tracker_manager.cpp \
|
torrent_info.cpp tracker_manager.cpp http_connection.cpp \
|
||||||
http_tracker_connection.cpp udp_tracker_connection.cpp \
|
http_tracker_connection.cpp udp_tracker_connection.cpp \
|
||||||
alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \
|
alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \
|
||||||
logger.cpp file_pool.cpp ut_pex.cpp \
|
logger.cpp file_pool.cpp ut_pex.cpp \
|
||||||
|
@ -40,6 +40,7 @@ $(top_srcdir)/include/libtorrent/file.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/file_pool.hpp \
|
$(top_srcdir)/include/libtorrent/file_pool.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/fingerprint.hpp \
|
$(top_srcdir)/include/libtorrent/fingerprint.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/hasher.hpp \
|
$(top_srcdir)/include/libtorrent/hasher.hpp \
|
||||||
|
$(top_srcdir)/include/libtorrent/http_connection.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/session_settings.hpp \
|
$(top_srcdir)/include/libtorrent/session_settings.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \
|
$(top_srcdir)/include/libtorrent/http_tracker_connection.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/identify_client.hpp \
|
$(top_srcdir)/include/libtorrent/identify_client.hpp \
|
||||||
|
@ -70,6 +71,7 @@ $(top_srcdir)/include/libtorrent/torrent_info.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/tracker_manager.hpp \
|
$(top_srcdir)/include/libtorrent/tracker_manager.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \
|
$(top_srcdir)/include/libtorrent/udp_tracker_connection.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/utf8.hpp \
|
$(top_srcdir)/include/libtorrent/utf8.hpp \
|
||||||
|
$(top_srcdir)/include/libtorrent/xml_parse.hpp \
|
||||||
$(top_srcdir)/include/libtorrent/version.hpp
|
$(top_srcdir)/include/libtorrent/version.hpp
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
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 "http_connection.hpp"
|
||||||
|
#include <asio/ip/tcp.hpp>
|
||||||
|
|
||||||
|
namespace libtorrent
|
||||||
|
{
|
||||||
|
|
||||||
|
void http_connection::get(std::string const& url, boost::posix_time::time_duration timeout)
|
||||||
|
{
|
||||||
|
std::string protocol;
|
||||||
|
std::string hostname;
|
||||||
|
std::string path;
|
||||||
|
int port;
|
||||||
|
boost::tie(protocol, hostname, port, path) = parse_url_components(url);
|
||||||
|
std::stringstream headers;
|
||||||
|
headers << "GET " << path << " HTTP/1.0\r\n"
|
||||||
|
"Host:" << hostname <<
|
||||||
|
"Connection: close\r\n"
|
||||||
|
"\r\n\r\n";
|
||||||
|
sendbuffer = headers.str();
|
||||||
|
start(hostname, boost::lexical_cast<std::string>(port), timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_connection::start(std::string const& hostname, std::string const& port
|
||||||
|
, boost::posix_time::time_duration timeout)
|
||||||
|
{
|
||||||
|
m_timeout = timeout;
|
||||||
|
m_timer.expires_from_now(m_timeout);
|
||||||
|
m_timer.async_wait(bind(&http_connection::on_timeout, shared_from_this(), _1));
|
||||||
|
if (m_hostname == hostname && m_port == port)
|
||||||
|
{
|
||||||
|
m_parser.reset();
|
||||||
|
asio::async_write(m_sock, asio::buffer(sendbuffer)
|
||||||
|
, bind(&http_connection::on_write, shared_from_this(), _1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_sock.close();
|
||||||
|
tcp::resolver::query query(hostname, port);
|
||||||
|
m_resolver.async_resolve(query, bind(&http_connection::on_resolve
|
||||||
|
, shared_from_this(), _1, _2));
|
||||||
|
m_hostname = hostname;
|
||||||
|
m_port = port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_connection::on_timeout(asio::error_code const& e)
|
||||||
|
{
|
||||||
|
if (e == asio::error::operation_aborted) return;
|
||||||
|
m_handler(asio::error::timed_out, m_parser, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_connection::close()
|
||||||
|
{
|
||||||
|
m_timer.cancel();
|
||||||
|
m_sock.close();
|
||||||
|
m_hostname.clear();
|
||||||
|
m_port.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_connection::on_resolve(asio::error_code const& e
|
||||||
|
, tcp::resolver::iterator i)
|
||||||
|
{
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
m_handler(e, m_parser, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
assert(i != tcp::resolver::iterator());
|
||||||
|
m_sock.async_connect(*i, boost::bind(&http_connection::on_connect
|
||||||
|
, shared_from_this(), _1/*, ++i*/));
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_connection::on_connect(asio::error_code const& e
|
||||||
|
/*, tcp::resolver::iterator i*/)
|
||||||
|
{
|
||||||
|
if (!e)
|
||||||
|
{
|
||||||
|
m_timer.expires_from_now(m_timeout);
|
||||||
|
m_timer.async_wait(bind(&http_connection::on_timeout, shared_from_this(), _1));
|
||||||
|
asio::async_write(m_sock, asio::buffer(sendbuffer)
|
||||||
|
, bind(&http_connection::on_write, shared_from_this(), _1));
|
||||||
|
}
|
||||||
|
/* else if (i != tcp::resolver::iterator())
|
||||||
|
{
|
||||||
|
// The connection failed. Try the next endpoint in the list.
|
||||||
|
m_sock.close();
|
||||||
|
m_sock.async_connect(*i, bind(&http_connection::on_connect
|
||||||
|
, shared_from_this(), _1, ++i));
|
||||||
|
}
|
||||||
|
*/ else
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
m_handler(e, m_parser, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_connection::on_write(asio::error_code const& e)
|
||||||
|
{
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
m_handler(e, m_parser, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string().swap(sendbuffer);
|
||||||
|
m_recvbuffer.resize(4096);
|
||||||
|
m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
|
||||||
|
, m_recvbuffer.size() - m_read_pos)
|
||||||
|
, bind(&http_connection::on_read, shared_from_this(), _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
void http_connection::on_read(asio::error_code const& e
|
||||||
|
, std::size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
if (e == asio::error::eof)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
m_handler(e, m_parser, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
m_handler(e, m_parser, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_read_pos += bytes_transferred;
|
||||||
|
assert(m_read_pos <= int(m_recvbuffer.size()));
|
||||||
|
|
||||||
|
if (m_bottled || !m_parser.header_finished())
|
||||||
|
{
|
||||||
|
libtorrent::buffer::const_interval rcv_buf(&m_recvbuffer[0]
|
||||||
|
, &m_recvbuffer[0] + m_read_pos);
|
||||||
|
m_parser.incoming(rcv_buf);
|
||||||
|
if (!m_bottled && m_parser.header_finished())
|
||||||
|
{
|
||||||
|
if (m_read_pos > m_parser.body_start())
|
||||||
|
m_handler(e, m_parser, &m_recvbuffer[0] + m_parser.body_start()
|
||||||
|
, m_read_pos - m_parser.body_start());
|
||||||
|
m_read_pos = 0;
|
||||||
|
m_timer.expires_from_now(m_timeout);
|
||||||
|
m_timer.async_wait(bind(&http_connection::on_timeout, shared_from_this(), _1));
|
||||||
|
}
|
||||||
|
else if (m_bottled && m_parser.finished())
|
||||||
|
{
|
||||||
|
m_handler(e, m_parser, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_handler(e, m_parser, &m_recvbuffer[0], m_read_pos);
|
||||||
|
m_read_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int(m_recvbuffer.size()) == m_read_pos)
|
||||||
|
m_recvbuffer.resize(std::min(m_read_pos + 2048, 1024*500));
|
||||||
|
if (m_read_pos == 1024 * 500)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
m_handler(asio::error::eof, m_parser, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos
|
||||||
|
, m_recvbuffer.size() - m_read_pos)
|
||||||
|
, bind(&http_connection::on_read
|
||||||
|
, shared_from_this(), _1, _2));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -176,7 +176,7 @@ namespace libtorrent
|
||||||
boost::get<1>(ret) += newline - pos;
|
boost::get<1>(ret) += newline - pos;
|
||||||
pos = newline;
|
pos = newline;
|
||||||
|
|
||||||
std::string::size_type separator = line.find(": ");
|
std::string::size_type separator = line.find(':');
|
||||||
if (separator == std::string::npos)
|
if (separator == std::string::npos)
|
||||||
{
|
{
|
||||||
// this means we got a blank line,
|
// this means we got a blank line,
|
||||||
|
@ -193,7 +193,12 @@ namespace libtorrent
|
||||||
|
|
||||||
std::string name = line.substr(0, separator);
|
std::string name = line.substr(0, separator);
|
||||||
std::transform(name.begin(), name.end(), name.begin(), &to_lower);
|
std::transform(name.begin(), name.end(), name.begin(), &to_lower);
|
||||||
std::string value = line.substr(separator + 2, std::string::npos);
|
++separator;
|
||||||
|
// skip whitespace
|
||||||
|
while (separator < line.size()
|
||||||
|
&& (line[separator] == ' ' || line[separator] == '\t'))
|
||||||
|
++separator;
|
||||||
|
std::string value = line.substr(separator, std::string::npos);
|
||||||
m_header.insert(std::make_pair(name, value));
|
m_header.insert(std::make_pair(name, value));
|
||||||
|
|
||||||
if (name == "content-length")
|
if (name == "content-length")
|
||||||
|
|
|
@ -250,6 +250,8 @@ void natpmp::on_reply(asio::error_code const& e
|
||||||
int public_port = read_uint16(in);
|
int public_port = read_uint16(in);
|
||||||
int lifetime = read_uint32(in);
|
int lifetime = read_uint32(in);
|
||||||
|
|
||||||
|
(void)time; // to remove warning
|
||||||
|
|
||||||
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
m_log << to_simple_string(microsec_clock::universal_time())
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
<< " <== port map response: " << (cmd - 128 == 1 ? "udp" : "tcp")
|
<< " <== port map response: " << (cmd - 128 == 1 ? "udp" : "tcp")
|
||||||
|
|
|
@ -1105,13 +1105,42 @@ namespace libtorrent
|
||||||
t->piece_failed(p.piece);
|
t->piece_failed(p.piece);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
pol.piece_finished(p.piece, verified);
|
pol.piece_finished(p.piece, verified);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
}
|
||||||
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
std::string err = e.what();
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!was_seed && t->is_seed())
|
if (!was_seed && t->is_seed())
|
||||||
{
|
{
|
||||||
assert(verified);
|
assert(verified);
|
||||||
t->completed();
|
t->completed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
}
|
||||||
|
catch (std::exception const& e)
|
||||||
|
{
|
||||||
|
std::string err = e.what();
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
|
|
@ -0,0 +1,545 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
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/pch.hpp"
|
||||||
|
|
||||||
|
#include "libtorrent/upnp.hpp"
|
||||||
|
#include "libtorrent/io.hpp"
|
||||||
|
#include "libtorrent/http_tracker_connection.hpp"
|
||||||
|
#include "libtorrent/xml_parse.hpp"
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/ref.hpp>
|
||||||
|
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||||
|
#include <asio/ip/host_name.hpp>
|
||||||
|
#include <asio/ip/multicast.hpp>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
using boost::bind;
|
||||||
|
using namespace libtorrent;
|
||||||
|
using boost::posix_time::microsec_clock;
|
||||||
|
using boost::posix_time::milliseconds;
|
||||||
|
using boost::posix_time::seconds;
|
||||||
|
|
||||||
|
enum { num_mappings = 2 };
|
||||||
|
|
||||||
|
// UPnP multicast address and port
|
||||||
|
address_v4 multicast_address = address_v4::from_string("239.255.255.250");
|
||||||
|
udp::endpoint multicast_endpoint(multicast_address, 1900);
|
||||||
|
|
||||||
|
upnp::upnp(io_service& ios, address const& listen_interface
|
||||||
|
, std::string const& user_agent, portmap_callback_t const& cb)
|
||||||
|
: m_user_agent(user_agent)
|
||||||
|
, m_callback(cb)
|
||||||
|
, m_retry_count(0)
|
||||||
|
, m_socket(ios)
|
||||||
|
, m_broadcast_timer(ios)
|
||||||
|
, m_refresh_timer(ios)
|
||||||
|
, m_disabled(false)
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log.open("upnp.log", std::ios::in | std::ios::out | std::ios::trunc);
|
||||||
|
#endif
|
||||||
|
rebind(listen_interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::rebind(address const& listen_interface)
|
||||||
|
{
|
||||||
|
if (listen_interface.is_v4() && listen_interface != address_v4::from_string("0.0.0.0"))
|
||||||
|
{
|
||||||
|
m_local_ip = listen_interface.to_v4();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// make a best guess of the interface we're using and its IP
|
||||||
|
udp::resolver r(m_socket.io_service());
|
||||||
|
udp::resolver::iterator i = r.resolve(udp::resolver::query(asio::ip::host_name(), "0"));
|
||||||
|
for (;i != udp::resolver_iterator(); ++i)
|
||||||
|
{
|
||||||
|
if (i->endpoint().address().is_v4()) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == udp::resolver_iterator())
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << "local host name did not resolve to an IPv4 address. "
|
||||||
|
"disabling UPnP" << std::endl;
|
||||||
|
#endif
|
||||||
|
m_disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_local_ip = i->endpoint().address().to_v4();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " local ip: " << m_local_ip.to_string() << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ((m_local_ip.to_ulong() & 0xff000000) != 0x0a000000
|
||||||
|
&& (m_local_ip.to_ulong() & 0xfff00000) != 0xac100000
|
||||||
|
&& (m_local_ip.to_ulong() & 0xffff0000) != 0xc0a80000)
|
||||||
|
{
|
||||||
|
// the local address seems to be an external
|
||||||
|
// internet address. Assume it is not behind a NAT
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << "not on a NAT. disabling UPnP" << std::endl;
|
||||||
|
#endif
|
||||||
|
m_disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using namespace asio::ip::multicast;
|
||||||
|
|
||||||
|
m_socket.open(udp::v4());
|
||||||
|
m_socket.set_option(datagram_socket::reuse_address(true));
|
||||||
|
m_socket.bind(udp::endpoint(m_local_ip, 0));
|
||||||
|
|
||||||
|
m_socket.set_option(join_group(multicast_address));
|
||||||
|
m_socket.set_option(outbound_interface(m_local_ip));
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (std::exception&)
|
||||||
|
{
|
||||||
|
m_disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_disabled = false;
|
||||||
|
|
||||||
|
m_retry_count = 0;
|
||||||
|
discover_device();
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::discover_device()
|
||||||
|
{
|
||||||
|
const char msearch[] =
|
||||||
|
"M-SEARCH * HTTP/1.1\r\n"
|
||||||
|
"HOST: 239.255.255.250:1900\r\n"
|
||||||
|
"ST:upnp:rootdevice\r\n"
|
||||||
|
"MAN:\"ssdp:discover\"\r\n"
|
||||||
|
"MX:3\r\n"
|
||||||
|
"\r\n\r\n";
|
||||||
|
|
||||||
|
m_socket.async_receive_from(asio::buffer(m_receive_buffer
|
||||||
|
, sizeof(m_receive_buffer)), m_remote, bind(&upnp::on_reply, this, _1, _2));
|
||||||
|
m_socket.send_to(asio::buffer(msearch, sizeof(msearch) - 1)
|
||||||
|
, multicast_endpoint);
|
||||||
|
|
||||||
|
++m_retry_count;
|
||||||
|
m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count));
|
||||||
|
m_broadcast_timer.async_wait(bind(&upnp::resend_request, this, _1));
|
||||||
|
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " ==> Broadcasting search for rootdevice" << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::set_mappings(int tcp, int udp)
|
||||||
|
{
|
||||||
|
if (m_disabled) return;
|
||||||
|
/*
|
||||||
|
update_mapping(0, tcp);
|
||||||
|
update_mapping(1, udp);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::resend_request(asio::error_code const& e)
|
||||||
|
{
|
||||||
|
using boost::posix_time::hours;
|
||||||
|
if (e) return;
|
||||||
|
if (m_retry_count >= 9)
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " *** Got no response in 9 retries. Giving up, "
|
||||||
|
"disabling UPnP." << std::endl;
|
||||||
|
#endif
|
||||||
|
// try again in two hours
|
||||||
|
m_disabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
discover_device();
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::on_reply(asio::error_code const& e
|
||||||
|
, std::size_t bytes_transferred)
|
||||||
|
{
|
||||||
|
using namespace libtorrent::detail;
|
||||||
|
using boost::posix_time::seconds;
|
||||||
|
if (e) return;
|
||||||
|
|
||||||
|
// since we're using udp, send the query 4 times
|
||||||
|
// just to make sure we find all devices
|
||||||
|
if (m_retry_count >= 4)
|
||||||
|
m_broadcast_timer.cancel();
|
||||||
|
|
||||||
|
// parse out the url for the device
|
||||||
|
|
||||||
|
/*
|
||||||
|
the response looks like this:
|
||||||
|
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
ST:upnp:rootdevice
|
||||||
|
USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice
|
||||||
|
Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc
|
||||||
|
Server: Custom/1.0 UPnP/1.0 Proc/Ver
|
||||||
|
EXT:
|
||||||
|
Cache-Control:max-age=180
|
||||||
|
DATE: Fri, 02 Jan 1970 08:10:38 GMT
|
||||||
|
*/
|
||||||
|
http_parser p;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
p.incoming(buffer::const_interval(m_receive_buffer
|
||||||
|
, m_receive_buffer + bytes_transferred));
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== Rootdevice responded with incorrect HTTP packet: "
|
||||||
|
<< e.what() << ". Ignoring device" << std::endl;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p.status_code() != 200)
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== Rootdevice responded with HTTP status: " << p.status_code()
|
||||||
|
<< ". Ignoring device" << std::endl;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p.header_finished())
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== Rootdevice responded with incomplete HTTP "
|
||||||
|
"packet. Ignoring device" << std::endl;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string url = p.header<std::string>("location");
|
||||||
|
if (url.empty())
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== Rootdevice response is missing a location header. "
|
||||||
|
"Ignoring device" << std::endl;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rootdevice d;
|
||||||
|
d.url = url;
|
||||||
|
|
||||||
|
std::set<rootdevice>::iterator i = m_devices.find(d);
|
||||||
|
|
||||||
|
if (i == m_devices.end())
|
||||||
|
{
|
||||||
|
|
||||||
|
std::string protocol;
|
||||||
|
// we don't have this device in our list. Add it
|
||||||
|
boost::tie(protocol, d.hostname, d.port, d.path)
|
||||||
|
= parse_url_components(d.url);
|
||||||
|
|
||||||
|
if (protocol != "http")
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== Rootdevice uses unsupported protocol: '" << protocol
|
||||||
|
<< "'. Ignoring device" << std::endl;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d.port == 0)
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== Rootdevice responded with a url with port 0. "
|
||||||
|
"Ignoring device" << std::endl;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== Found rootdevice: " << d.url << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
boost::tie(i, boost::tuples::ignore) = m_devices.insert(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i->control_url.empty())
|
||||||
|
{
|
||||||
|
// we don't have a WANIP or WANPPP url for this device,
|
||||||
|
// ask for it
|
||||||
|
rootdevice& d = const_cast<rootdevice&>(*i);
|
||||||
|
d.upnp_connection.reset(new http_connection(m_socket.io_service()
|
||||||
|
, boost::bind(&upnp::on_upnp_xml, this, _1, _2, boost::ref(d))));
|
||||||
|
d.upnp_connection->get(d.url);
|
||||||
|
}
|
||||||
|
else if (!i->ports_mapped)
|
||||||
|
{
|
||||||
|
// TODO: initiate a port map operation if any is pending
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::post(rootdevice& d, std::stringstream const& soap
|
||||||
|
, std::string const& soap_action)
|
||||||
|
{
|
||||||
|
std::stringstream header;
|
||||||
|
|
||||||
|
header << "POST " << d.control_url << " HTTP/1.1\r\n"
|
||||||
|
"Host: " << d.hostname << ":" << d.port << "\r\n"
|
||||||
|
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
||||||
|
"Content-Length: " << soap.str().size() << "\r\n"
|
||||||
|
"Soapaction: \"" << d.service_namespace << "#" << soap_action << "\"\r\n\r\n" << soap.str();
|
||||||
|
|
||||||
|
d.upnp_connection->sendbuffer = header.str();
|
||||||
|
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
|
||||||
|
, seconds(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::map_port(rootdevice& d, int i)
|
||||||
|
{
|
||||||
|
d.upnp_connection.reset(new http_connection(m_socket.io_service()
|
||||||
|
, boost::bind(&upnp::on_upnp_map_response, this, _1, _2
|
||||||
|
, boost::ref(d))));
|
||||||
|
|
||||||
|
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>" << 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>";
|
||||||
|
|
||||||
|
post(d, soap, soap_action);
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " ==> AddPortMapping: " << soap.str() << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct parse_state
|
||||||
|
{
|
||||||
|
parse_state(): found_service(false), exit(false) {}
|
||||||
|
void reset(char const* st)
|
||||||
|
{
|
||||||
|
found_service = false;
|
||||||
|
exit = false;
|
||||||
|
service_type = st;
|
||||||
|
}
|
||||||
|
bool found_service;
|
||||||
|
bool exit;
|
||||||
|
std::string top_tag;
|
||||||
|
std::string control_url;
|
||||||
|
char const* service_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
void find_control_url(int type, char const* string, parse_state& state)
|
||||||
|
{
|
||||||
|
if (state.exit) return;
|
||||||
|
|
||||||
|
if (type == xml_start_tag)
|
||||||
|
{
|
||||||
|
if ((!state.top_tag.empty() && state.top_tag == "service")
|
||||||
|
|| !strcmp(string, "service"))
|
||||||
|
{
|
||||||
|
state.top_tag = string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (type == xml_end_tag)
|
||||||
|
{
|
||||||
|
if (!strcmp(string, "service"))
|
||||||
|
{
|
||||||
|
state.top_tag.clear();
|
||||||
|
if (state.found_service) state.exit = true;
|
||||||
|
}
|
||||||
|
else if (!state.top_tag.empty() && state.top_tag != "service")
|
||||||
|
state.top_tag = "service";
|
||||||
|
}
|
||||||
|
else if (type == xml_string)
|
||||||
|
{
|
||||||
|
if (state.top_tag == "serviceType")
|
||||||
|
{
|
||||||
|
if (!strcmp(string, state.service_type))
|
||||||
|
state.found_service = true;
|
||||||
|
}
|
||||||
|
else if (state.top_tag == "controlURL")
|
||||||
|
{
|
||||||
|
state.control_url = string;
|
||||||
|
if (state.found_service) state.exit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::on_upnp_xml(asio::error_code const& e
|
||||||
|
, libtorrent::http_parser const& p, rootdevice& d)
|
||||||
|
{
|
||||||
|
parse_state s;
|
||||||
|
s.reset("urn:schemas-upnp-org:service:WANIPConnection:1");
|
||||||
|
xml_parse((char*)p.get_body().begin, (char*)p.get_body().end
|
||||||
|
, boost::bind(&find_control_url, _1, _2, boost::ref(s)));
|
||||||
|
d.service_namespace = "urn:schemas-upnp-org:service:WANIPConnection:1";
|
||||||
|
if (!s.found_service)
|
||||||
|
{
|
||||||
|
// we didn't find the WAN IP connection, look for
|
||||||
|
// a PPP IP connection
|
||||||
|
s.reset("urn:schemas-upnp-org:service:PPPIPConnection:1");
|
||||||
|
xml_parse((char*)p.get_body().begin, (char*)p.get_body().end
|
||||||
|
, boost::bind(&find_control_url, _1, _2, boost::ref(s)));
|
||||||
|
d.service_namespace = "urn:schemas-upnp-org:service:WANPPPConnection:1";
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== Rootdevice response, found control URL: " << s.control_url << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
d.control_url = s.control_url;
|
||||||
|
d.upnp_connection.reset();
|
||||||
|
|
||||||
|
map_port(d, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
struct error_code_parse_state
|
||||||
|
{
|
||||||
|
error_code_parse_state(): in_error_code(false), exit(false), error_code(-1) {}
|
||||||
|
bool in_error_code;
|
||||||
|
bool exit;
|
||||||
|
int error_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
void find_error_code(int type, char const* string, error_code_parse_state& state)
|
||||||
|
{
|
||||||
|
if (state.exit) return;
|
||||||
|
if (type == xml_start_tag && !strcmp("errorCode", string))
|
||||||
|
{
|
||||||
|
state.in_error_code = true;
|
||||||
|
}
|
||||||
|
else if (type == xml_string && state.in_error_code)
|
||||||
|
{
|
||||||
|
state.error_code = std::atoi(string);
|
||||||
|
state.exit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::on_upnp_map_response(asio::error_code const& e
|
||||||
|
, libtorrent::http_parser const& p, rootdevice& d)
|
||||||
|
{
|
||||||
|
if (e)
|
||||||
|
{
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== error while adding portmap: " << e << std::endl;
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// error code response may look like this:
|
||||||
|
// <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
// s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
|
||||||
|
// <s:Body>
|
||||||
|
// <s:Fault>
|
||||||
|
// <faultcode>s:Client</faultcode>
|
||||||
|
// <faultstring>UPnPError</faultstring>
|
||||||
|
// <detail>
|
||||||
|
// <UPnPErrorxmlns="urn:schemas-upnp-org:control-1-0">
|
||||||
|
// <errorCode>402</errorCode>
|
||||||
|
// <errorDescription>Invalid Args</errorDescription>
|
||||||
|
// </UPnPError>
|
||||||
|
// </detail>
|
||||||
|
// </s:Fault>
|
||||||
|
// </s:Body>
|
||||||
|
// </s:Envelope>
|
||||||
|
|
||||||
|
|
||||||
|
error_code_parse_state s;
|
||||||
|
xml_parse((char*)p.get_body().begin, (char*)p.get_body().end
|
||||||
|
, bind(&find_error_code, _1, _2, boost::ref(s)));
|
||||||
|
|
||||||
|
#if defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)
|
||||||
|
if (s.error_code != -1)
|
||||||
|
{
|
||||||
|
m_log << to_simple_string(microsec_clock::universal_time())
|
||||||
|
<< " <== got error message: " << s.error_code << std::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (s.error_code == 725)
|
||||||
|
{
|
||||||
|
d.lease_duration = 0;
|
||||||
|
map_port(d, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << std::string(p.get_body().begin, p.get_body().end) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void upnp::close()
|
||||||
|
{
|
||||||
|
if (m_disabled) return;
|
||||||
|
m_socket.close();
|
||||||
|
std::for_each(m_devices.begin(), m_devices.end()
|
||||||
|
, bind(&rootdevice::close, _1));
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue