forked from premiere/premiere-libtorrent
495 lines
14 KiB
C++
495 lines
14 KiB
C++
/*
|
|
|
|
Copyright (c) 2009-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.
|
|
|
|
*/
|
|
|
|
#ifndef TORRENT_UTP_STREAM_HPP_INCLUDED
|
|
#define TORRENT_UTP_STREAM_HPP_INCLUDED
|
|
|
|
#include "libtorrent/proxy_base.hpp"
|
|
#include "libtorrent/udp_socket.hpp"
|
|
#include "libtorrent/io.hpp"
|
|
#include "libtorrent/packet_buffer.hpp"
|
|
#include "libtorrent/error_code.hpp"
|
|
#include "libtorrent/time.hpp"
|
|
#include "libtorrent/close_reason.hpp"
|
|
|
|
#include <functional>
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
#include "libtorrent/aux_/disable_warnings_push.hpp"
|
|
#include <boost/system/system_error.hpp>
|
|
#include "libtorrent/aux_/disable_warnings_pop.hpp"
|
|
#endif
|
|
|
|
namespace libtorrent
|
|
{
|
|
#ifndef TORRENT_UTP_LOG_ENABLE
|
|
#define TORRENT_UTP_LOG 0
|
|
#define TORRENT_VERBOSE_UTP_LOG 0
|
|
#else
|
|
#define TORRENT_UTP_LOG 1
|
|
#define TORRENT_VERBOSE_UTP_LOG 1
|
|
#endif
|
|
|
|
#if TORRENT_UTP_LOG
|
|
TORRENT_FORMAT(1, 2)
|
|
void utp_log(char const* fmt, ...);
|
|
TORRENT_EXPORT bool is_utp_stream_logging();
|
|
|
|
// This function should be used at the very beginning and very end of your program.
|
|
TORRENT_EXPORT void set_utp_stream_logging(bool enable);
|
|
#endif
|
|
|
|
TORRENT_EXTRA_EXPORT bool compare_less_wrap(std::uint32_t lhs
|
|
, std::uint32_t rhs, std::uint32_t mask);
|
|
|
|
struct utp_socket_manager;
|
|
|
|
// internal: the point of the bif_endian_int is two-fold
|
|
// one purpose is to not have any alignment requirements
|
|
// so that any buffer received from the network can be cast
|
|
// to it and read as an integer of various sizes without
|
|
// triggering a bus error. The other purpose is to convert
|
|
// from network byte order to host byte order when read and
|
|
// written, to offer a convenient interface to both interpreting
|
|
// and writing network packets
|
|
template <class T> struct big_endian_int
|
|
{
|
|
big_endian_int& operator=(T v)
|
|
{
|
|
char* p = m_storage;
|
|
detail::write_impl<T>(v, p);
|
|
return *this;
|
|
}
|
|
operator T() const
|
|
{
|
|
const char* p = m_storage;
|
|
return detail::read_impl(p, detail::type<T>());
|
|
}
|
|
private:
|
|
char m_storage[sizeof(T)];
|
|
};
|
|
|
|
typedef big_endian_int<std::uint64_t> be_uint64;
|
|
typedef big_endian_int<std::uint32_t> be_uint32;
|
|
typedef big_endian_int<std::uint16_t> be_uint16;
|
|
typedef big_endian_int<std::int64_t> be_int64;
|
|
typedef big_endian_int<std::int32_t> be_int32;
|
|
typedef big_endian_int<std::int16_t> be_int16;
|
|
|
|
/*
|
|
uTP header from BEP 29
|
|
|
|
0 4 8 16 24 32
|
|
+-------+-------+---------------+---------------+---------------+
|
|
| type | ver | extension | connection_id |
|
|
+-------+-------+---------------+---------------+---------------+
|
|
| timestamp_microseconds |
|
|
+---------------+---------------+---------------+---------------+
|
|
| timestamp_difference_microseconds |
|
|
+---------------+---------------+---------------+---------------+
|
|
| wnd_size |
|
|
+---------------+---------------+---------------+---------------+
|
|
| seq_nr | ack_nr |
|
|
+---------------+---------------+---------------+---------------+
|
|
|
|
*/
|
|
|
|
// internal: the different kinds of uTP packets
|
|
enum utp_socket_state_t
|
|
{ ST_DATA, ST_FIN, ST_STATE, ST_RESET, ST_SYN, NUM_TYPES };
|
|
|
|
// internal: extension headers. 2 is skipped because there is a deprecated
|
|
// extension with that number in the wild
|
|
enum utp_extensions_t
|
|
{ utp_no_extension = 0, utp_sack = 1, utp_close_reason = 3 };
|
|
|
|
struct utp_header
|
|
{
|
|
std::uint8_t type_ver;
|
|
std::uint8_t extension;
|
|
be_uint16 connection_id;
|
|
be_uint32 timestamp_microseconds;
|
|
be_uint32 timestamp_difference_microseconds;
|
|
be_uint32 wnd_size;
|
|
be_uint16 seq_nr;
|
|
be_uint16 ack_nr;
|
|
|
|
int get_type() const { return type_ver >> 4; }
|
|
int get_version() const { return type_ver & 0xf; }
|
|
};
|
|
|
|
struct utp_socket_impl;
|
|
|
|
utp_socket_impl* construct_utp_impl(std::uint16_t recv_id
|
|
, std::uint16_t send_id, void* userdata
|
|
, utp_socket_manager* sm);
|
|
void detach_utp_impl(utp_socket_impl* s);
|
|
void delete_utp_impl(utp_socket_impl* s);
|
|
bool should_delete(utp_socket_impl* s);
|
|
void tick_utp_impl(utp_socket_impl* s, time_point now);
|
|
void utp_init_mtu(utp_socket_impl* s, int link_mtu, int utp_mtu);
|
|
bool utp_incoming_packet(utp_socket_impl* s, span<char const> p
|
|
, udp::endpoint const& ep, time_point receive_time);
|
|
bool utp_match(utp_socket_impl* s, udp::endpoint const& ep, std::uint16_t id);
|
|
udp::endpoint utp_remote_endpoint(utp_socket_impl* s);
|
|
std::uint16_t utp_receive_id(utp_socket_impl* s);
|
|
int utp_socket_state(utp_socket_impl const* s);
|
|
void utp_send_ack(utp_socket_impl* s);
|
|
void utp_socket_drained(utp_socket_impl* s);
|
|
void utp_writable(utp_socket_impl* s);
|
|
|
|
// this is the user-level stream interface to utp sockets.
|
|
// the reason why it's split up in a utp_stream class and
|
|
// an implementation class is because the socket state has
|
|
// to be able to out-live the user level socket. For instance
|
|
// when sending data on a stream and then closing it, the
|
|
// state holding the send buffer has to be kept around until
|
|
// it has been flushed, which may be longer than the client
|
|
// will keep the utp_stream object around for.
|
|
// for more details, see utp_socket_impl, which is analogous
|
|
// to the kernel state for a socket. It's defined in utp_stream.cpp
|
|
struct TORRENT_EXTRA_EXPORT utp_stream
|
|
{
|
|
using lowest_layer_type = utp_stream ;
|
|
using endpoint_type = tcp::socket::endpoint_type;
|
|
using protocol_type = tcp::socket::protocol_type;
|
|
|
|
explicit utp_stream(io_service& io_service);
|
|
~utp_stream();
|
|
utp_stream& operator=(utp_stream const&) = delete;
|
|
utp_stream(utp_stream const&) = delete;
|
|
utp_stream& operator=(utp_stream&&) = default;
|
|
utp_stream(utp_stream&&) = default;
|
|
|
|
lowest_layer_type& lowest_layer() { return *this; }
|
|
|
|
// used for incoming connections
|
|
void set_impl(utp_socket_impl* s);
|
|
utp_socket_impl* get_impl();
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
template <class IO_Control_Command>
|
|
void io_control(IO_Control_Command&) {}
|
|
#endif
|
|
|
|
template <class IO_Control_Command>
|
|
void io_control(IO_Control_Command&, error_code&) {}
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
void bind(endpoint_type const& /*endpoint*/) {}
|
|
#endif
|
|
|
|
void bind(endpoint_type const&, error_code&);
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
template <class SettableSocketOption>
|
|
void set_option(SettableSocketOption const&) {}
|
|
#endif
|
|
|
|
template <class SettableSocketOption>
|
|
error_code set_option(SettableSocketOption const&, error_code& ec) { return ec; }
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
template <class GettableSocketOption>
|
|
void get_option(GettableSocketOption&) {}
|
|
#endif
|
|
|
|
template <class GettableSocketOption>
|
|
error_code get_option(GettableSocketOption&, error_code& ec)
|
|
{ return ec; }
|
|
|
|
error_code cancel(error_code&)
|
|
{
|
|
cancel_handlers(boost::asio::error::operation_aborted);
|
|
return error_code();
|
|
}
|
|
|
|
void close();
|
|
void close(error_code const&) { close(); }
|
|
|
|
void set_close_reason(close_reason_t code);
|
|
close_reason_t get_close_reason();
|
|
|
|
bool is_open() const { return m_open; }
|
|
|
|
int read_buffer_size() const;
|
|
static void on_read(void* self, std::size_t bytes_transferred
|
|
, error_code const& ec, bool kill);
|
|
static void on_write(void* self, std::size_t bytes_transferred
|
|
, error_code const& ec, bool kill);
|
|
static void on_connect(void* self, error_code const& ec, bool kill);
|
|
static void on_close_reason(void* self, close_reason_t reason);
|
|
|
|
void add_read_buffer(void* buf, size_t len);
|
|
void issue_read();
|
|
void add_write_buffer(void const* buf, size_t len);
|
|
void issue_write();
|
|
size_t read_some(bool clear_buffers);
|
|
|
|
int send_delay() const;
|
|
int recv_delay() const;
|
|
|
|
void do_connect(tcp::endpoint const& ep);
|
|
|
|
endpoint_type local_endpoint() const
|
|
{
|
|
error_code ec;
|
|
return local_endpoint(ec);
|
|
}
|
|
|
|
endpoint_type local_endpoint(error_code& ec) const;
|
|
|
|
endpoint_type remote_endpoint() const
|
|
{
|
|
error_code ec;
|
|
return remote_endpoint(ec);
|
|
}
|
|
|
|
endpoint_type remote_endpoint(error_code& ec) const;
|
|
|
|
std::size_t available() const;
|
|
std::size_t available(error_code& /*ec*/) const { return available(); }
|
|
|
|
io_service& get_io_service() { return m_io_service; }
|
|
|
|
template <class Handler>
|
|
void async_connect(endpoint_type const& endpoint, Handler const& handler)
|
|
{
|
|
if (!endpoint.address().is_v4())
|
|
{
|
|
m_io_service.post(std::bind<void>(handler, boost::asio::error::operation_not_supported, 0));
|
|
return;
|
|
}
|
|
|
|
if (m_impl == 0)
|
|
{
|
|
m_io_service.post(std::bind<void>(handler, boost::asio::error::not_connected, 0));
|
|
return;
|
|
}
|
|
|
|
m_connect_handler = handler;
|
|
do_connect(endpoint);
|
|
}
|
|
|
|
template <class Mutable_Buffers, class Handler>
|
|
void async_read_some(Mutable_Buffers const& buffers, Handler const& handler)
|
|
{
|
|
if (m_impl == 0)
|
|
{
|
|
m_io_service.post(std::bind<void>(handler, boost::asio::error::not_connected, 0));
|
|
return;
|
|
}
|
|
|
|
TORRENT_ASSERT(!m_read_handler);
|
|
if (m_read_handler)
|
|
{
|
|
m_io_service.post(std::bind<void>(handler, boost::asio::error::operation_not_supported, 0));
|
|
return;
|
|
}
|
|
std::size_t bytes_added = 0;
|
|
for (typename Mutable_Buffers::const_iterator i = buffers.begin()
|
|
, end(buffers.end()); i != end; ++i)
|
|
{
|
|
if (buffer_size(*i) == 0) continue;
|
|
using boost::asio::buffer_cast;
|
|
using boost::asio::buffer_size;
|
|
add_read_buffer(buffer_cast<void*>(*i), buffer_size(*i));
|
|
bytes_added += buffer_size(*i);
|
|
}
|
|
if (bytes_added == 0)
|
|
{
|
|
// if we're reading 0 bytes, post handler immediately
|
|
// asio's SSL layer depends on this behavior
|
|
m_io_service.post(std::bind<void>(handler, error_code(), 0));
|
|
return;
|
|
}
|
|
|
|
m_read_handler = handler;
|
|
issue_read();
|
|
}
|
|
|
|
template <class Handler>
|
|
void async_read_some(null_buffers const&, Handler const& handler)
|
|
{
|
|
if (m_impl == 0)
|
|
{
|
|
m_io_service.post(std::bind<void>(handler, boost::asio::error::not_connected, 0));
|
|
return;
|
|
}
|
|
|
|
TORRENT_ASSERT(!m_read_handler);
|
|
if (m_read_handler)
|
|
{
|
|
TORRENT_ASSERT_FAIL(); // we should never do this!
|
|
m_io_service.post(std::bind<void>(handler, boost::asio::error::operation_not_supported, 0));
|
|
return;
|
|
}
|
|
m_read_handler = handler;
|
|
issue_read();
|
|
}
|
|
|
|
void do_async_connect(endpoint_type const& ep
|
|
, std::function<void(error_code const&)> const& handler);
|
|
|
|
template <class Protocol>
|
|
void open(Protocol const&, error_code&)
|
|
{ m_open = true; }
|
|
|
|
template <class Protocol>
|
|
void open(Protocol const&)
|
|
{ m_open = true; }
|
|
|
|
template <class Mutable_Buffers>
|
|
std::size_t read_some(Mutable_Buffers const& buffers, error_code& ec)
|
|
{
|
|
TORRENT_ASSERT(!m_read_handler);
|
|
if (m_impl == 0)
|
|
{
|
|
ec = boost::asio::error::not_connected;
|
|
return 0;
|
|
}
|
|
|
|
if (read_buffer_size() == 0)
|
|
{
|
|
ec = boost::asio::error::would_block;
|
|
return 0;
|
|
}
|
|
#if TORRENT_USE_ASSERTS
|
|
size_t buf_size = 0;
|
|
#endif
|
|
|
|
for (typename Mutable_Buffers::const_iterator i = buffers.begin()
|
|
, end(buffers.end()); i != end; ++i)
|
|
{
|
|
using boost::asio::buffer_cast;
|
|
using boost::asio::buffer_size;
|
|
add_read_buffer(buffer_cast<void*>(*i), buffer_size(*i));
|
|
#if TORRENT_USE_ASSERTS
|
|
buf_size += buffer_size(*i);
|
|
#endif
|
|
}
|
|
std::size_t ret = read_some(true);
|
|
TORRENT_ASSERT(ret <= buf_size);
|
|
TORRENT_ASSERT(ret > 0);
|
|
return ret;
|
|
}
|
|
|
|
template <class Const_Buffers>
|
|
std::size_t write_some(Const_Buffers const& /* buffers */, error_code& /* ec */)
|
|
{
|
|
TORRENT_ASSERT(false && "not implemented!");
|
|
// TODO: implement blocking write. Low priority since it's not used (yet)
|
|
return 0;
|
|
}
|
|
|
|
#ifndef BOOST_NO_EXCEPTIONS
|
|
template <class Mutable_Buffers>
|
|
std::size_t read_some(Mutable_Buffers const& buffers)
|
|
{
|
|
error_code ec;
|
|
std::size_t ret = read_some(buffers, ec);
|
|
if (ec)
|
|
boost::throw_exception(boost::system::system_error(ec));
|
|
return ret;
|
|
}
|
|
|
|
template <class Const_Buffers>
|
|
std::size_t write_some(Const_Buffers const& buffers)
|
|
{
|
|
error_code ec;
|
|
std::size_t ret = write_some(buffers, ec);
|
|
if (ec)
|
|
boost::throw_exception(boost::system::system_error(ec));
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
template <class Const_Buffers, class Handler>
|
|
void async_write_some(Const_Buffers const& buffers, Handler const& handler)
|
|
{
|
|
if (m_impl == 0)
|
|
{
|
|
m_io_service.post(std::bind<void>(handler
|
|
, boost::asio::error::not_connected, 0));
|
|
return;
|
|
}
|
|
|
|
TORRENT_ASSERT(!m_write_handler);
|
|
if (m_write_handler)
|
|
{
|
|
m_io_service.post(std::bind<void>(handler
|
|
, boost::asio::error::operation_not_supported, 0));
|
|
return;
|
|
}
|
|
|
|
std::size_t bytes_added = 0;
|
|
for (typename Const_Buffers::const_iterator i = buffers.begin()
|
|
, end(buffers.end()); i != end; ++i)
|
|
{
|
|
if (buffer_size(*i) == 0) continue;
|
|
using boost::asio::buffer_cast;
|
|
using boost::asio::buffer_size;
|
|
add_write_buffer(buffer_cast<void const*>(*i), buffer_size(*i));
|
|
bytes_added += buffer_size(*i);
|
|
}
|
|
if (bytes_added == 0)
|
|
{
|
|
// if we're writing 0 bytes, post handler immediately
|
|
// asio's SSL layer depends on this behavior
|
|
m_io_service.post(std::bind<void>(handler, error_code(), 0));
|
|
return;
|
|
}
|
|
m_write_handler = handler;
|
|
issue_write();
|
|
}
|
|
|
|
private:
|
|
|
|
void cancel_handlers(error_code const&);
|
|
|
|
std::function<void(error_code const&)> m_connect_handler;
|
|
std::function<void(error_code const&, std::size_t)> m_read_handler;
|
|
std::function<void(error_code const&, std::size_t)> m_write_handler;
|
|
|
|
io_service& m_io_service;
|
|
utp_socket_impl* m_impl;
|
|
|
|
close_reason_t m_incoming_close_reason = close_reason_t::none;
|
|
|
|
// this field requires another 8 bytes (including padding)
|
|
bool m_open;
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|