merged RC_1_1

This commit is contained in:
arvidn 2016-02-23 23:48:29 -05:00
commit 0b173c6651
19 changed files with 594 additions and 126 deletions

View File

@ -91,6 +91,9 @@ script:
- cd ../examples
- bjam --hash -j3 warnings-as-errors=on variant=$variant $toolset link=shared
- if [[ lang == "cpp11" ]]; then
bjam --hash -j3 warnings-as-errors=on variant=$variant $toolset link=shared bt-get bt-get2;
fi
- cd ..
# build libtorrent separately and install it in a temporary (well known) dir

View File

@ -118,6 +118,9 @@ build_script:
- if not defined x64 (
cd %ROOT_DIRECTORY%\examples
& b2.exe --hash -j2 %compiler% variant=%variant% linkflags=%linkflags% include=%include% link=shared
& if %compiler% == msvc-14.0 ( b2.exe --hash -j2 %compiler% variant=%variant% linkflags=%linkflags% include=%include% link=shared bt-get bt-get2 )
& cd %ROOT_DIRECTORY%\examples
& b2.exe --hash -j2 %compiler% variant=%variant% linkflags=%linkflags% include=%include% link=shared
& cd %ROOT_DIRECTORY%\bindings\python
& b2.exe --hash -j2 %compiler% stage_module install-dependencies=on variant=%variant% libtorrent-link=shared linkflags=%linkflags% include=%include%
& python test.py

View File

@ -149,6 +149,7 @@ def is_visible(desc):
return True
def highlight_signature(s):
s = s.replace('TORRENT_OVERRIDE', 'override').replace('TORRENT_FINAL', 'final')
name = s.split('(', 1)
name2 = name[0].split(' ')
if len(name2[-1]) == 0: return s
@ -273,7 +274,8 @@ def parse_class(lno, lines, filename):
state = 'private'
class_type = 'class'
name = decl.split(':')[0].replace('class ', '').replace('struct ', '').replace('TORRENT_FINAL', '').strip()
decl = decl.replace('TORRENT_FINAL', 'final')
name = decl.split(':')[0].replace('class ', '').replace('struct ', '').replace('final', '').strip()
while lno < len(lines):

View File

@ -29,7 +29,7 @@ For example:
#include <libtorrent/session.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/torrent_handle.hpp>
namespace lt = libtorrent;
int main(int argc, char const* argv[])
{
@ -70,46 +70,165 @@ completes downloading, we can poll the session for alerts periodically and print
them out, as well as listening for the torrent_finished_alert_, which is posted
when a torrent completes.
.. include:: ../examples/bt-get.cpp
:code: c++
:tab-width: 2
:start-after: */
alert masks
-----------
The output from this program will be quite verbose, which is probably a good
starting point to get some understanding of what's going on. Alerts are
categorized into alert categories. Each category can be enabled and disabled
independently via the *alert mask*.
The alert mask is a configuration option offered by libtorrent. There are many
configuration options, see settings_pack_. The alert_mask_ setting is an integer
of the `category flags`_ ORed together.
For instance, to only see the most pertinent alerts, the session can be
constructed like this:
.. code:: c++
#include <iostream>
#include <libtorrent/session.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/alert_types.hpp>
namespace lt = libtorrent;
int main(int argc, char const* argv[])
lt::settings_pack pack;
pack.set_int(lt::settings_pack::alert_mask
, lt::alert::error_notification
| lt::alert::storage_notification
| lt::alert::status_notification);
lt::session ses(pack);
Configuration options can be updated after the session is started by calling
`apply_settings()`_. Some settings are best set before starting the session
though, like listen_interfaces_, to avoid race conditions. If you start the
session with the default settings and then immediately change them, there will
still be a window where the default settings apply.
Changing the settings may trigger listen sockets to close and re-open and
NAT-PMP, UPnP updates to be sent. For this reason, it's typically a good idea
to batch settings updates into a single call.
session destruction
-------------------
The session destructor is blocking by default. When shutting down, trackers
will need to be contacted to stop torrents and other outstanding operations
need to be cancelled. Shutting down can sometimes take several seconds,
primarily because of trackers that are unresponsive (and time out) and also
DNS servers that are unresponsive. DNS lookups are especially difficult to
abort when stalled.
In order to be able to start destruction an wait for it asynchronously, one
can call `session::abort()`_.
This call returns a session_proxy_ object, which is a handle keeping the session
state alive while destructing it. It deliberately does not provide any of the
session operations, since it's shutting down.
After having a session_proxy_ object, the session destructor does not block.
However, the session_proxy_ destructor *will*.
This can be used to shut down multiple sessions or other parts of the
application in parallel.
asynchronous operations
-----------------------
Essentially any call to a member function of session_ or torrent_handle_ that
returns a value is a blocking synchronous call. Meaning it will post a message
to the main libtorrent thread and wait for a response. Such calls may be
expensive, and in applications where stalls should be avoided (such as user
interface threads), blocking calls should be avoided.
In the example above, session::add_torrent() returns a torrent_handle_ and is
thus blocking. For higher efficiency, `async_add_torrent()`_ will post a message
to the main thread to add a torrent, and post the resulting torrent_handle_ back
in an alert (add_torrent_alert_). This is especially useful when adding a lot
of torrents in quick succession, as there's no stall in between calls.
In the example above, we don't actually use the torrent_handle_ for anything, so
converting it to use `async_add_torrent()`_ is just a matter of replacing the
`add_torrent()`_ call with `async_add_torrent()`_.
torrent_status_updates
----------------------
To get updates to the status of torrents, call `post_torrent_updates()`_ on the
session object. This will cause libtorrent to post a state_update_alert_
containing torrent_status_ objects for all torrents whose status has *changed*
since the last call to `post_torrent_updates()`_.
The state_update_alert_ looks something like this:
.. code:: c++
struct state_update_alert : alert
{
if (argc != 2) {
std::cerr << "usage: " << argv[0] << " <magnet-url>" << std::endl;
return 1;
}
lt::session ses;
virtual std::string message() const;
std::vector<torrent_status> status;
};
lt::add_torrent_params atp;
atp.url = argv[1];
atp.save_path = "."; // save in current dir
lt::torrent_handle h = ses.add_torrent(atp);
The ``status`` field only contains the torrent_status_ for torrents with
updates since the last call. It may be empty if no torrent has updated its
state. This feature is critical for scalability_.
bool done = false;
while (!done) {
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
See the torrent_status_ object for more information on what is in there.
Perhaps the most interesting fields are ``total_payload_download``,
``total_payload_upload``, ``num_peers`` and ``state``.
for (lt::alert const* a : alerts) {
std::cout << a->message() << std::endl;
if (lt::alert_cast<lt::torrent_finished_alert>(a)) {
done = true;
}
}
}
}
resuming torrents
-----------------
*TODO* cover async_add_torrent()
*TODO* cover post_torrent_updates()
*TODO* cover save_resume_data()
Since bittorrent downloads pieces of files in random order, it's not trivial to
resume a partial download. When resuming a download, the bittorrent engine must
restore the state of the downloading torrent, specifically, which parts of the
file(s) are downloaded. There are two approaches to doing this:
1. read every piece of the downloaded files from disk and compare it against the
expected hash.
2. save to disk the state of which pieces (and partial pieces) are downloaded,
and load it back in again when resuming.
If no resume data is provided with a torrent that's added, libtorrent will
employ (1) by default.
To save resume data, call `save_resume_data()`_ on the torrent_handle_ object.
This will ask libtorrent to generate the resume data and post it back in
a save_resume_data_alert_. If generating the resume data fails for any reason,
a save_resume_data_failed_alert_ is posted instead. Exactly one of those alerts
will be posted for every call to `save_resume_data()`_.
The save_resume_data_alert_ looks something like this:
.. code:: c++
struct save_resume_data_alert : torrent_alert
{
virtual std::string message() const;
// points to the resume data.
boost::shared_ptr<entry> resume_data;
};
``resume_data`` points to an entry_ object. This represents a node or a tree
of nodes in a bencoded_ structure, which is the native encoding scheme in
bittorrent. It can be encoded into a byte buffer or file using `bencode()`_.
example
-------
Here's an updated version of the above example with the following updates:
1. not using blocking calls
2. printing torrent status updates rather than the raw log
3. saving and loading resume files
.. include:: ../examples/bt-get2.cpp
:code: c++
:tab-width: 2
:start-after: */
.. _session: reference-Core.html#session
.. _session_handle: reference-Core.html#session_handle
@ -121,5 +240,22 @@ when a torrent completes.
.. _`alert`: reference-Alerts.html#alert
.. _`alert_cast<>`: reference-Alerts.html#alert_cast()
.. _torrent_finished_alert: reference-Alerts.html#torrent-finished-alert
.. _listen_interfaces: reference-Settings.html#listen_interfaces
.. _`add_torrent_alert`: reference-Alerts.html#add-torrent-alert
.. _settings_pack: reference-Settings.html#settings_pack
.. _alert_mask: reference-Settings.html#alert_mask
.. _`category flags`: reference-Alerts.html#category_t
.. _`apply_settings()`: reference-Core.html#apply_settings()
.. _`session::abort()`: reference-Core.html#abort()
.. _session_proxy: reference-Core.html#session_proxy
.. _`post_torrent_updates()`: reference-Core.html#post_torrent_updates()
.. _torrent_status: reference-Core.html#torrent_status
.. _state_update_alert: reference-Alerts.html#state_update_alert
.. _scalability: http://blog.libtorrent.org/2011/11/scalable-interfaces/
.. _`save_resume_data()`: reference-Core.html#save_resume_data()
.. _save_resume_data_alert: reference-Alerts.html#save_resume_data_alert
.. _save_resume_data_failed_alert: reference-Alerts.html#save_resume_data_failed_alert
.. _bencoded: https://en.wikipedia.org/wiki/Bencode
.. _entry: reference-Bencoding.html#entry
.. _`bencode()`: reference-Bencoding.html#bencode()

View File

@ -20,6 +20,8 @@ project client_test
exe client_test : client_test.cpp print.cpp torrent_view.cpp session_view.cpp ;
exe simple_client : simple_client.cpp ;
exe bt-get : bt-get.cpp ;
exe bt-get2 : bt-get2.cpp ;
exe stats_counters : stats_counters.cpp ;
exe dump_torrent : dump_torrent.cpp ;
exe make_torrent : make_torrent.cpp ;
@ -28,6 +30,8 @@ exe upnp_test : upnp_test.cpp ;
explicit stage_client_test ;
explicit stage_connection_tester ;
explicit bt-get ;
explicit bt-get2 ;
install stage_client_test : client_test : <location>. ;
install stage_connection_tester : connection_tester : <location>. ;

View File

@ -5,6 +5,8 @@ example_programs = \
make_torrent \
simple_client \
upnp_test \
bt_get \
bt_get2 \
connection_tester
if ENABLE_EXAMPLES
@ -15,25 +17,14 @@ EXTRA_PROGRAMS = $(example_programs)
EXTRA_DIST = Jamfile CMakeLists.txt run_cmake.sh.in cmake/FindLibtorrentRasterbar.cmake
client_test_SOURCES = client_test.cpp print.cpp session_view.cpp torrent_view.cpp
#client_test_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la
stats_counters_SOURCES = stats_counters.cpp
#stats_counters_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la
bt_get_SOURCES = bt-get.cpp
bt_get2_SOURCES = bt-get2.cpp
dump_torrent_SOURCES = dump_torrent.cpp
#dump_torrent_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la
make_torrent_SOURCES = make_torrent.cpp
#make_torrent_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la
simple_client_SOURCES = simple_client.cpp
#simple_client_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la
connection_tester_SOURCES = connection_tester.cpp
#connection_tester_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la
upnp_test_SOURCES = upnp_test.cpp
#upnp_test_LDADD = $(top_builddir)/src/libtorrent-rasterbar.la
LDADD = $(top_builddir)/src/libtorrent-rasterbar.la

75
examples/bt-get.cpp Normal file
View File

@ -0,0 +1,75 @@
/*
Copyright (c) 2016, 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 <iostream>
#include <thread>
#include <chrono>
#include <libtorrent/session.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/alert_types.hpp>
namespace lt = libtorrent;
int main(int argc, char const* argv[])
{
if (argc != 2) {
std::cerr << "usage: " << argv[0] << " <magnet-url>" << std::endl;
return 1;
}
lt::session ses;
lt::add_torrent_params atp;
atp.url = argv[1];
atp.save_path = "."; // save in current dir
lt::torrent_handle h = ses.add_torrent(atp);
for (;;) {
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
for (lt::alert const* a : alerts) {
std::cout << a->message() << std::endl;
// if we receive the finished alert or an error, we're done
if (lt::alert_cast<lt::torrent_finished_alert>(a)) {
goto done;
}
if (lt::alert_cast<lt::torrent_error_alert>(a)) {
goto done;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
done:
std::cout << "done, shutting down" << std::endl;
}

142
examples/bt-get2.cpp Normal file
View File

@ -0,0 +1,142 @@
/*
Copyright (c) 2016, 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 <iostream>
#include <thread>
#include <chrono>
#include <fstream>
#include <libtorrent/session.hpp>
#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/torrent_status.hpp>
namespace lt = libtorrent;
// return the name of a torrent status enum
char const* state(lt::torrent_status::state_t s)
{
switch(s)
{
case lt::torrent_status::checking_files: return "checking";
case lt::torrent_status::downloading_metadata: return "dl metadata";
case lt::torrent_status::downloading: return "downloading";
case lt::torrent_status::finished: return "finished";
case lt::torrent_status::seeding: return "seeding";
case lt::torrent_status::allocating: return "allocating";
case lt::torrent_status::checking_resume_data: return "checking resume";
default: return "<>";
}
}
int main(int argc, char const* argv[])
{
if (argc != 2) {
std::cerr << "usage: " << argv[0] << " <magnet-url>" << std::endl;
return 1;
}
lt::settings_pack pack;
pack.set_int(lt::settings_pack::alert_mask
, lt::alert::error_notification
| lt::alert::storage_notification
| lt::alert::status_notification);
lt::session ses(pack);
lt::add_torrent_params atp;
// load resume data from disk and pass it in as we add the magnet link
std::ifstream ifs(".resume_file", std::ios_base::binary);
ifs.unsetf(std::ios_base::skipws);
atp.resume_data.assign(std::istream_iterator<char>(ifs)
, std::istream_iterator<char>());
atp.url = argv[1];
atp.save_path = "."; // save in current dir
ses.async_add_torrent(atp);
// this is the handle we'll set once we get the notification of it being
// added
lt::torrent_handle h;
for (;;) {
std::vector<lt::alert*> alerts;
ses.pop_alerts(&alerts);
for (lt::alert const* a : alerts) {
if (auto at = lt::alert_cast<lt::add_torrent_alert>(a)) {
h = at->handle;
}
// if we receive the finished alert or an error, we're done
if (lt::alert_cast<lt::torrent_finished_alert>(a)) {
h.save_resume_data();
goto done;
}
if (lt::alert_cast<lt::torrent_error_alert>(a)) {
std::cout << a->message() << std::endl;
goto done;
}
// when resume data is ready, save it
if (auto rd = lt::alert_cast<lt::save_resume_data_alert>(a)) {
std::ofstream of(".resume_file", std::ios_base::binary);
of.unsetf(std::ios_base::skipws);
lt::bencode(std::ostream_iterator<char>(of)
, *rd->resume_data);
}
if (auto st = lt::alert_cast<lt::state_update_alert>(a)) {
if (st->status.empty()) continue;
// we only have a single torrent, so we know which one
// the status is for
lt::torrent_status const& s = st->status[0];
std::cout << "\r" << state(s.state) << " "
<< (s.download_payload_rate / 1000) << " kB/s "
<< (s.total_done / 1000) << " kB ("
<< (s.progress_ppm / 10000) << "%) downloaded\x1b[K";
std::cout.flush();
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
// ask the session to post a state_update_alert, to update our
// state output for the torrent
ses.post_torrent_updates();
// TODO: 3 call save_resume_data() once every 30 seconds or so
}
done:
std::cout << "\ndone, shutting down" << std::endl;
}

View File

@ -1366,7 +1366,7 @@ namespace libtorrent
struct TORRENT_EXPORT portmap_alert TORRENT_FINAL : alert
{
// internal
portmap_alert(aux::stack_allocator& alloc, int i, int port, int t);
portmap_alert(aux::stack_allocator& alloc, int i, int port, int t, int protocol);
TORRENT_DEFINE_ALERT(portmap_alert, 51)
@ -1382,6 +1382,15 @@ namespace libtorrent
// 0 for NAT-PMP and 1 for UPnP.
int map_type;
enum protocol_t
{
tcp,
udp
};
// the protocol this mapping was for. one of protocol_t enums
int protocol;
};
#ifndef TORRENT_DISABLE_LOGGING
@ -1942,8 +1951,7 @@ namespace libtorrent
TORRENT_DEFINE_ALERT(dht_error_alert, 73)
static const int static_category = alert::error_notification
| alert::dht_notification;
static const int static_category = alert::error_notification | alert::dht_notification;
virtual std::string message() const TORRENT_OVERRIDE;
// the error code

View File

@ -353,7 +353,7 @@ namespace libtorrent
, std::vector<address> const& addresses, int port);
#endif
void maybe_update_udp_mapping(int nat, int local_port, int external_port);
void maybe_update_udp_mapping(int nat, bool ssl, int local_port, int external_port);
#if !defined(TORRENT_DISABLE_ENCRYPTION) && !defined(TORRENT_DISABLE_EXTENSIONS)
torrent const* find_encrypted_torrent(
@ -372,7 +372,7 @@ namespace libtorrent
// called when a port mapping is successful, or a router returns
// a failure to map a port
void on_port_mapping(int mapping, address const& ip, int port
, error_code const& ec, int nat_transport);
, int protocol, error_code const& ec, int nat_transport);
bool is_aborted() const TORRENT_OVERRIDE { return m_abort; }
bool is_paused() const TORRENT_OVERRIDE { return m_paused; }

View File

@ -43,7 +43,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/aux_/disable_warnings_push.hpp"
#include <boost/function/function1.hpp>
#include <boost/function/function4.hpp>
#include <boost/function/function5.hpp>
#include <boost/enable_shared_from_this.hpp>
#include "libtorrent/aux_/disable_warnings_pop.hpp"
@ -54,7 +54,7 @@ namespace libtorrent
// int: port mapping index
// int: external port
// std::string: error message
typedef boost::function<void(int, address, int, error_code const&)> portmap_callback_t;
typedef boost::function<void(int, address, int, int, error_code const&)> portmap_callback_t;
typedef boost::function<void(char const*)> log_callback_t;
class natpmp : public boost::enable_shared_from_this<natpmp>

View File

@ -662,9 +662,8 @@ namespace libtorrent
only_if_modified = 4
};
// ``save_resume_data()`` generates fast-resume data and returns it as an
// entry. This entry is suitable for being bencoded. For more information
// about how fast-resume works, see fast-resume_.
// ``save_resume_data()`` asks libtorrent to generate fast-resume data for
// this torrent.
//
// The ``flags`` argument is a bitmask of flags ORed together. see
// save_resume_flags_t
@ -676,9 +675,7 @@ namespace libtorrent
// The fast resume data will be empty in the following cases:
//
// 1. The torrent handle is invalid.
// 2. The torrent is checking (or is queued for checking) its storage, it
// will obviously not be ready to write resume data.
// 3. The torrent hasn't received valid metadata and was started without
// 2. The torrent hasn't received valid metadata and was started without
// metadata (see libtorrent's metadata-from-peers_ extension)
//
// Note that by the time you receive the fast resume data, it may already

View File

@ -78,14 +78,7 @@ namespace libtorrent
, dont_queue = 4
};
bool is_open() const
{
return m_ipv4_sock.is_open()
#if TORRENT_USE_IPV6
|| m_ipv6_sock.is_open()
#endif
;
}
bool is_open() const { return m_abort == false; }
io_service& get_io_service() { return m_ipv4_sock.get_io_service(); }
void subscribe(udp_socket_observer* o);
@ -143,7 +136,7 @@ namespace libtorrent
udp::endpoint proxy_addr() const { return m_proxy_addr; }
protected:
private:
struct queued_packet
{
@ -169,12 +162,12 @@ namespace libtorrent
;
}
private:
// non-copyable
udp_socket(udp_socket const&);
udp_socket& operator=(udp_socket const&);
void close_impl();
// observers on this udp socket
std::vector<udp_socket_observer*> m_observers;
std::vector<udp_socket_observer*> m_added_observers;

View File

@ -43,7 +43,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/resolver.hpp"
#include <boost/function/function1.hpp>
#include <boost/function/function4.hpp>
#include <boost/function/function5.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
@ -100,9 +100,7 @@ namespace libtorrent
// int: external port
// std::string: error message
// an empty string as error means success
// a port-mapping index of -1 means it's
// an informational log message
typedef boost::function<void(int, address, int, error_code const&)> portmap_callback_t;
typedef boost::function<void(int, address, int, int, error_code const&)> portmap_callback_t;
typedef boost::function<void(char const*)> log_callback_t;
struct parse_state

View File

@ -774,6 +774,8 @@ namespace libtorrent {
static char const* const nat_type_str[] = {"NAT-PMP", "UPnP"};
static char const* const protocol_str[] = {"TCP", "UDP"};
static char const* const socket_type_str[] = {
"null",
"TCP",
@ -907,15 +909,16 @@ namespace libtorrent {
+ ": " + convert_from_native(error.message());
}
portmap_alert::portmap_alert(aux::stack_allocator&, int i, int port, int t)
: mapping(i), external_port(port), map_type(t)
portmap_alert::portmap_alert(aux::stack_allocator&, int i, int port, int t
, int proto)
: mapping(i), external_port(port), map_type(t), protocol(proto)
{}
std::string portmap_alert::message() const
{
char ret[200];
snprintf(ret, sizeof(ret), "successfully mapped port using %s. external port: %u"
, nat_type_str[map_type], external_port);
snprintf(ret, sizeof(ret), "successfully mapped port using %s. external port: %s/%u"
, nat_type_str[map_type], protocol_str[protocol], external_port);
return ret;
}

View File

@ -185,10 +185,11 @@ void natpmp::disable(error_code const& ec, mutex::scoped_lock& l)
, end(m_mappings.end()); i != end; ++i)
{
if (i->protocol == none) continue;
int const proto = i->protocol;
i->protocol = none;
int index = i - m_mappings.begin();
l.unlock();
m_callback(index, address(), 0, ec);
m_callback(index, address(), 0, proto, ec);
l.lock();
}
close_impl(l);
@ -563,15 +564,18 @@ void natpmp::on_reply(error_code const& e
if (result >= 1 && result <= 5) ev = errors[result - 1];
m->expires = aux::time_now() + hours(2);
int const proto = m->protocol;
l.unlock();
m_callback(index, address(), 0, error_code(ev, get_libtorrent_category()));
m_callback(index, address(), 0, proto
, error_code(ev, get_libtorrent_category()));
l.lock();
}
else if (m->action == mapping_t::action_add)
{
int const proto = m->protocol;
l.unlock();
m_callback(index, m_external_ip, m->external_port,
error_code(errors::no_error, get_libtorrent_category()));
m_callback(index, m_external_ip, m->external_port, proto
, error_code(errors::no_error, get_libtorrent_category()));
l.lock();
}

View File

@ -2000,7 +2000,8 @@ namespace aux {
else
{
created_ssl_udp_socket = true;
// TODO: 3 port map SSL udp socket here
maybe_update_udp_mapping(0, true, bind_ep.port(), bind_ep.port());
maybe_update_udp_mapping(1, true, bind_ep.port(), bind_ep.port());
}
} while (ec == error_code(error::address_in_use) && retries > 0);
}
@ -2035,8 +2036,8 @@ namespace aux {
{
created_udp_socket = true;
m_external_udp_port = m_udp_socket.local_port();
maybe_update_udp_mapping(0, bind_ep.port(), bind_ep.port());
maybe_update_udp_mapping(1, bind_ep.port(), bind_ep.port());
maybe_update_udp_mapping(0, false, bind_ep.port(), bind_ep.port());
maybe_update_udp_mapping(1, false, bind_ep.port(), bind_ep.port());
}
} while (ec == error_code(error::address_in_use) && retries > 0);
}
@ -2046,11 +2047,39 @@ namespace aux {
// previous one
#ifdef TORRENT_USE_OPENSSL
if (!created_ssl_udp_socket)
{
m_ssl_udp_socket.close();
// if there are mappings for the SSL socket, delete them now
if (m_ssl_udp_mapping[0] != -1 && m_natpmp)
{
m_natpmp->delete_mapping(m_ssl_udp_mapping[0]);
m_ssl_udp_mapping[0] = -1;
}
if (m_ssl_udp_mapping[1] != -1 && m_upnp)
{
m_upnp->delete_mapping(m_ssl_udp_mapping[1]);
m_ssl_udp_mapping[1] = -1;
}
}
#endif
if (!created_udp_socket)
{
m_udp_socket.close();
// if there are mappings for the socket, delete them now
if (m_udp_mapping[0] != -1 && m_natpmp)
{
m_natpmp->delete_mapping(m_udp_mapping[0]);
m_udp_mapping[0] = -1;
}
if (m_udp_mapping[1] != -1 && m_upnp)
{
m_upnp->delete_mapping(m_udp_mapping[1]);
m_udp_mapping[1] = -1;
}
}
// we made it! now post all the listen_succeeded_alerts
for (std::list<listen_socket_t>::iterator i = m_listen_sockets.begin()
@ -5241,6 +5270,11 @@ namespace aux {
boost::uint16_t session_impl::ssl_listen_port() const
{
#ifdef TORRENT_USE_OPENSSL
// honor the SSL listen port being disabled
if (m_settings.get_int(settings_pack::ssl_listen) == 0)
return 0;
// if peer connections are set up to be received over a socks
// proxy, and it's the same one as we're using for the tracker
// just tell the tracker the socks5 port we're listening on
@ -5317,7 +5351,7 @@ namespace aux {
// transport is 0 for NAT-PMP and 1 for UPnP
void session_impl::on_port_mapping(int mapping, address const& ip, int port
, error_code const& ec, int map_transport)
, int const protocol, error_code const& ec, int map_transport)
{
TORRENT_ASSERT(is_single_thread());
@ -5334,7 +5368,8 @@ namespace aux {
m_external_udp_port = port;
if (m_alerts.should_post<portmap_alert>())
m_alerts.emplace_alert<portmap_alert>(mapping, port
, map_transport);
, map_transport, protocol == natpmp::udp
? portmap_alert::udp : portmap_alert::tcp);
return;
}
@ -5361,7 +5396,8 @@ namespace aux {
if (!ec && m_alerts.should_post<portmap_alert>())
{
m_alerts.emplace_alert<portmap_alert>(mapping, port
, map_transport);
, map_transport, protocol == natpmp::udp
? portmap_alert::udp : portmap_alert::tcp);
}
}
@ -5759,38 +5795,45 @@ namespace aux {
#endif
void session_impl::maybe_update_udp_mapping(int nat, int local_port, int external_port)
void session_impl::maybe_update_udp_mapping(int const nat, bool const ssl
, int const local_port, int const external_port)
{
int local, external, protocol;
#ifdef TORRENT_USE_OPENSSL
int* mapping = ssl ? m_ssl_udp_mapping : m_udp_mapping;
#else
TORRENT_UNUSED(ssl);
int* mapping = m_udp_mapping;
#endif
if (nat == 0 && m_natpmp)
{
if (m_udp_mapping[nat] != -1)
if (mapping[nat] != -1)
{
if (m_natpmp->get_mapping(m_udp_mapping[nat], local, external, protocol))
if (m_natpmp->get_mapping(mapping[nat], local, external, protocol))
{
// we already have a mapping. If it's the same, don't do anything
if (local == local_port && external == external_port && protocol == natpmp::udp)
return;
}
m_natpmp->delete_mapping(m_udp_mapping[nat]);
m_natpmp->delete_mapping(mapping[nat]);
}
m_udp_mapping[nat] = m_natpmp->add_mapping(natpmp::udp
mapping[nat] = m_natpmp->add_mapping(natpmp::udp
, local_port, external_port);
return;
}
else if (nat == 1 && m_upnp)
{
if (m_udp_mapping[nat] != -1)
if (mapping[nat] != -1)
{
if (m_upnp->get_mapping(m_udp_mapping[nat], local, external, protocol))
if (m_upnp->get_mapping(mapping[nat], local, external, protocol))
{
// we already have a mapping. If it's the same, don't do anything
if (local == local_port && external == external_port && protocol == natpmp::udp)
return;
}
m_upnp->delete_mapping(m_udp_mapping[nat]);
m_upnp->delete_mapping(mapping[nat]);
}
m_udp_mapping[nat] = m_upnp->add_mapping(upnp::udp
mapping[nat] = m_upnp->add_mapping(upnp::udp
, local_port, external_port);
return;
}
@ -6489,7 +6532,7 @@ namespace aux {
// into the session_impl.
m_natpmp = boost::make_shared<natpmp>(boost::ref(m_io_service)
, boost::bind(&session_impl::on_port_mapping
, this, _1, _2, _3, _4, 0)
, this, _1, _2, _3, _4, _5, 0)
, boost::bind(&session_impl::on_port_map_log
, this, _1, 0));
m_natpmp->start();
@ -6537,7 +6580,7 @@ namespace aux {
m_upnp = boost::make_shared<upnp>(boost::ref(m_io_service)
, m_settings.get_str(settings_pack::user_agent)
, boost::bind(&session_impl::on_port_mapping
, this, _1, _2, _3, _4, 1)
, this, _1, _2, _3, _4, _5, 1)
, boost::bind(&session_impl::on_port_map_log
, this, _1, 1)
, m_settings.get_bool(settings_pack::upnp_ignore_nonrouters));

View File

@ -78,7 +78,7 @@ udp_socket::udp_socket(io_service& ios)
, m_queue_packets(false)
, m_tunnel_packets(false)
, m_force_proxy(false)
, m_abort(false)
, m_abort(true)
, m_outstanding_ops(0)
#if TORRENT_USE_IPV6
, m_v6_write_subscribed(false)
@ -307,7 +307,11 @@ void udp_socket::on_read(error_code const& ec, udp::socket* s)
}
return;
}
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
CHECK_MAGIC;
@ -493,7 +497,11 @@ void udp_socket::on_read_impl(udp::endpoint const& ep
return;
}
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
return;
}
@ -516,7 +524,11 @@ void udp_socket::on_read_impl(udp::endpoint const& ep
void udp_socket::setup_read(udp::socket* s)
{
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
#if TORRENT_USE_IPV6
if (s == &m_ipv6_sock)
@ -774,17 +786,13 @@ void udp_socket::bind(udp::endpoint const& ep, error_code& ec)
CHECK_MAGIC;
TORRENT_ASSERT(is_single_thread());
TORRENT_ASSERT(m_abort == false);
if (m_abort)
{
ec = boost::asio::error::operation_aborted;
return;
}
m_abort = false;
if (m_ipv4_sock.is_open()) m_ipv4_sock.close(ec);
#if TORRENT_USE_IPV6
if (m_ipv6_sock.is_open()) m_ipv6_sock.close(ec);
#endif
ec.clear();
if (ep.address().is_v4())
{
@ -858,7 +866,11 @@ void udp_socket::set_proxy_settings(aux::proxy_settings const& ps)
m_proxy_settings = ps;
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
if (ps.type == settings_pack::socks5
|| ps.type == settings_pack::socks5_pw)
@ -880,6 +892,19 @@ void udp_socket::set_proxy_settings(aux::proxy_settings const& ps)
}
}
void udp_socket::close_impl()
{
if (m_outstanding_ops == 0)
{
error_code ec;
m_ipv4_sock.close(ec);
#if TORRENT_USE_IPV6
m_ipv6_sock.close(ec);
#endif
m_socks5_sock.close(ec);
}
}
void udp_socket::on_name_lookup(error_code const& e, tcp::resolver::iterator i)
{
#if defined TORRENT_ASIO_DEBUGGING
@ -897,7 +922,12 @@ void udp_socket::on_name_lookup(error_code const& e, tcp::resolver::iterator i)
+ m_outstanding_resolve
+ m_outstanding_socks);
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
CHECK_MAGIC;
if (e == boost::asio::error::operation_aborted) return;
@ -976,7 +1006,11 @@ void udp_socket::on_connect_timeout(error_code const& ec)
m_queue_packets = false;
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
CHECK_MAGIC;
TORRENT_ASSERT(is_single_thread());
@ -1009,7 +1043,11 @@ void udp_socket::on_connected(error_code const& e)
if (e == boost::asio::error::operation_aborted) return;
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
if (e)
{
@ -1068,7 +1106,11 @@ void udp_socket::handshake1(error_code const& e)
+ m_outstanding_timeout
+ m_outstanding_resolve
+ m_outstanding_socks);
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
CHECK_MAGIC;
if (e)
{
@ -1104,7 +1146,11 @@ void udp_socket::handshake2(error_code const& e)
+ m_outstanding_timeout
+ m_outstanding_resolve
+ m_outstanding_socks);
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
CHECK_MAGIC;
if (e)
@ -1185,7 +1231,11 @@ void udp_socket::handshake3(error_code const& e)
+ m_outstanding_timeout
+ m_outstanding_resolve
+ m_outstanding_socks);
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
CHECK_MAGIC;
if (e)
{
@ -1221,7 +1271,11 @@ void udp_socket::handshake4(error_code const& e)
+ m_outstanding_timeout
+ m_outstanding_resolve
+ m_outstanding_socks);
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
CHECK_MAGIC;
if (e)
{
@ -1287,7 +1341,11 @@ void udp_socket::connect1(error_code const& e)
+ m_outstanding_timeout
+ m_outstanding_resolve
+ m_outstanding_socks);
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
CHECK_MAGIC;
if (e)
{
@ -1394,7 +1452,11 @@ void udp_socket::hung_up(error_code const& e)
+ m_outstanding_timeout
+ m_outstanding_resolve
+ m_outstanding_socks);
if (m_abort) return;
if (m_abort)
{
close_impl();
return;
}
CHECK_MAGIC;
TORRENT_ASSERT(is_single_thread());

View File

@ -1041,9 +1041,10 @@ void upnp::disable(error_code const& ec, mutex::scoped_lock& l)
, end(m_mappings.end()); i != end; ++i)
{
if (i->protocol == none) continue;
int const proto = i->protocol;
i->protocol = none;
l.unlock();
m_callback(i - m_mappings.begin(), address(), 0, ec);
m_callback(i - m_mappings.begin(), address(), 0, proto, ec);
l.lock();
}
@ -1372,7 +1373,7 @@ void upnp::on_upnp_map_response(error_code const& e
if (s.error_code == -1)
{
l.unlock();
m_callback(mapping, d.external_ip, m.external_port, error_code());
m_callback(mapping, d.external_ip, m.external_port, m.protocol, error_code());
l.lock();
if (d.lease_duration > 0)
{
@ -1414,8 +1415,9 @@ void upnp::return_error(int mapping, int code, mutex::scoped_lock& l)
error_string += ": ";
error_string += e->msg;
}
const int proto = m_mappings[mapping].protocol;
l.unlock();
m_callback(mapping, address(), 0, error_code(code, get_upnp_category()));
m_callback(mapping, address(), 0, proto, error_code(code, get_upnp_category()));
l.lock();
}
@ -1468,8 +1470,10 @@ void upnp::on_upnp_unmap_response(error_code const& e
, boost::bind(&find_error_code, _1, _2, boost::ref(s)));
}
int const proto = m_mappings[mapping].protocol;
l.unlock();
m_callback(mapping, address(), 0, p.status_code() != 200
m_callback(mapping, address(), 0, proto, p.status_code() != 200
? error_code(p.status_code(), get_http_category())
: error_code(s.error_code, get_upnp_category()));
l.lock();