2016-11-29 16:23:38 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2016 Charybdis Development Team
|
|
|
|
* Copyright (C) 2016 Jason Volk <jason@zemos.net>
|
|
|
|
*
|
|
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
|
|
* copyright notice and this permission notice is present in all copies.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
2017-03-11 04:31:20 +01:00
|
|
|
#define HAVE_IRCD_CLIENT_SOCKET_H
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-09-21 04:31:54 +02:00
|
|
|
/// This file is not included with the IRCd standard include stack because
|
|
|
|
/// it requires symbols we can't forward declare without boost headers. It
|
|
|
|
/// is part of the <ircd/asio.h> stack which can be included in your
|
|
|
|
/// definition file if you need low level access to this socket API. The
|
|
|
|
/// client.h still offers higher level access to sockets without requiring
|
|
|
|
/// boost headers; please check that for satisfaction before including this.
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
namespace ircd
|
|
|
|
{
|
|
|
|
namespace asio = boost::asio;
|
|
|
|
namespace ip = asio::ip;
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
using boost::system::error_code;
|
|
|
|
using asio::steady_timer;
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
IRCD_EXCEPTION(error, nxdomain)
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
struct socket;
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
extern asio::ssl::context sslv23_client;
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
std::string string(const ip::address &);
|
|
|
|
ip::address address(const ip::tcp::endpoint &);
|
|
|
|
std::string hostaddr(const ip::tcp::endpoint &);
|
|
|
|
uint16_t port(const ip::tcp::endpoint &);
|
2017-03-13 22:07:58 +01:00
|
|
|
|
2017-09-12 18:28:41 +02:00
|
|
|
size_t write(socket &, const ilist<const_buffer> &); // write_all
|
|
|
|
size_t write(socket &, const iov<const_buffer> &); // write_all
|
|
|
|
size_t write(socket &, const const_buffer &); // write_all
|
|
|
|
size_t write(socket &, iov<const_buffer> &); // write_some
|
|
|
|
|
|
|
|
size_t read(socket &, const ilist<mutable_buffer> &); // read_all
|
|
|
|
size_t read(socket &, const iov<mutable_buffer> &); // read_all
|
|
|
|
size_t read(socket &, const mutable_buffer &); // read_all
|
|
|
|
size_t read(socket &, iov<mutable_buffer> &); // read_some
|
|
|
|
|
|
|
|
size_t available(const socket &);
|
|
|
|
bool connected(const socket &) noexcept;
|
2017-08-28 23:51:22 +02:00
|
|
|
}
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
struct ircd::socket
|
|
|
|
:std::enable_shared_from_this<ircd::socket>
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
|
|
|
struct init;
|
|
|
|
struct stat;
|
2017-03-10 22:23:01 +01:00
|
|
|
struct scope_timeout;
|
2016-11-29 16:23:38 +01:00
|
|
|
struct io;
|
|
|
|
|
|
|
|
enum dc
|
|
|
|
{
|
2017-08-23 23:49:33 +02:00
|
|
|
RST, // hardest disconnect
|
|
|
|
FIN, // graceful shutdown both directions
|
|
|
|
FIN_SEND, // graceful shutdown send side
|
|
|
|
FIN_RECV, // graceful shutdown recv side
|
|
|
|
SSL_NOTIFY, // SSL close_notify (async, errors ignored)
|
|
|
|
SSL_NOTIFY_YIELD, // SSL close_notify (yields context, throws)
|
2016-11-29 16:23:38 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
struct stat
|
|
|
|
{
|
|
|
|
size_t bytes {0};
|
|
|
|
size_t calls {0};
|
|
|
|
};
|
|
|
|
|
|
|
|
using message_flags = boost::asio::socket_base::message_flags;
|
|
|
|
using handshake_type = asio::ssl::stream<ip::tcp::socket>::handshake_type;
|
|
|
|
using handler = std::function<void (const error_code &) noexcept>;
|
|
|
|
|
|
|
|
asio::ssl::stream<ip::tcp::socket> ssl;
|
|
|
|
ip::tcp::socket &sd;
|
|
|
|
steady_timer timer;
|
|
|
|
stat in, out;
|
|
|
|
bool timedout;
|
|
|
|
|
2017-08-23 22:47:15 +02:00
|
|
|
void call_user(const handler &, const error_code &) noexcept;
|
|
|
|
bool handle_error(const error_code &ec);
|
|
|
|
void handle_timeout(std::weak_ptr<socket> wp, const error_code &ec);
|
|
|
|
void handle(std::weak_ptr<socket>, handler, const error_code &, const size_t &) noexcept;
|
2016-11-29 16:23:38 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
operator const ip::tcp::socket &() const { return sd; }
|
|
|
|
operator ip::tcp::socket &() { return sd; }
|
|
|
|
|
|
|
|
ip::tcp::endpoint remote() const { return sd.remote_endpoint(); }
|
|
|
|
ip::tcp::endpoint local() const { return sd.local_endpoint(); }
|
|
|
|
|
|
|
|
template<class iov> auto read_some(const iov &, error_code &);
|
|
|
|
template<class iov> auto read_some(const iov &);
|
|
|
|
template<class iov> auto read(const iov &, error_code &);
|
|
|
|
template<class iov> auto read(const iov &);
|
|
|
|
|
|
|
|
template<class iov> auto write_some(const iov &, error_code &);
|
|
|
|
template<class iov> auto write_some(const iov &);
|
|
|
|
template<class iov> auto write(const iov &, error_code &);
|
|
|
|
template<class iov> auto write(const iov &);
|
|
|
|
|
2017-09-12 18:28:41 +02:00
|
|
|
void set_timeout(const milliseconds &, handler);
|
|
|
|
void set_timeout(const milliseconds &);
|
|
|
|
|
2016-11-29 16:23:38 +01:00
|
|
|
// Asynchronous 'ready' closure
|
|
|
|
void operator()(const milliseconds &timeout, handler);
|
|
|
|
void operator()(handler);
|
|
|
|
void cancel();
|
|
|
|
|
2017-09-12 18:28:41 +02:00
|
|
|
bool connected() const noexcept;
|
2017-08-23 23:49:33 +02:00
|
|
|
void disconnect(const dc &type = dc::SSL_NOTIFY);
|
2017-03-10 22:23:01 +01:00
|
|
|
void connect(const ip::tcp::endpoint &ep, const milliseconds &timeout = -1ms);
|
2016-11-29 16:23:38 +01:00
|
|
|
|
|
|
|
socket(const std::string &host,
|
|
|
|
const uint16_t &port,
|
2017-03-10 22:23:01 +01:00
|
|
|
const milliseconds &timeout = -1ms,
|
2016-11-29 16:23:38 +01:00
|
|
|
asio::ssl::context &ssl = sslv23_client,
|
|
|
|
boost::asio::io_service *const &ios = ircd::ios);
|
|
|
|
|
|
|
|
socket(const ip::tcp::endpoint &remote,
|
2017-03-10 22:23:01 +01:00
|
|
|
const milliseconds &timeout = -1ms,
|
2016-11-29 16:23:38 +01:00
|
|
|
asio::ssl::context &ssl = sslv23_client,
|
|
|
|
boost::asio::io_service *const &ios = ircd::ios);
|
|
|
|
|
|
|
|
socket(asio::ssl::context &ssl = sslv23_client,
|
|
|
|
boost::asio::io_service *const &ios = ircd::ios);
|
|
|
|
|
2017-08-23 22:47:15 +02:00
|
|
|
socket(socket &&) = delete;
|
|
|
|
socket(const socket &) = delete;
|
2016-11-29 16:23:38 +01:00
|
|
|
~socket() noexcept;
|
|
|
|
};
|
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
class ircd::socket::scope_timeout
|
2017-03-10 22:23:01 +01:00
|
|
|
{
|
|
|
|
socket *s;
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-03-10 22:23:01 +01:00
|
|
|
public:
|
|
|
|
scope_timeout(socket &, const milliseconds &timeout, const socket::handler &handler);
|
|
|
|
scope_timeout(socket &, const milliseconds &timeout);
|
|
|
|
scope_timeout(const scope_timeout &) = delete;
|
|
|
|
scope_timeout &operator=(const scope_timeout &) = delete;
|
|
|
|
~scope_timeout() noexcept;
|
|
|
|
};
|
2016-11-29 16:23:38 +01:00
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
class ircd::socket::io
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
|
|
|
struct socket &sock;
|
|
|
|
struct stat &stat;
|
|
|
|
size_t bytes;
|
|
|
|
|
|
|
|
public:
|
|
|
|
operator size_t() const;
|
|
|
|
|
|
|
|
io(struct socket &, struct stat &, const std::function<size_t ()> &closure);
|
|
|
|
};
|
|
|
|
|
2017-08-28 23:51:22 +02:00
|
|
|
struct ircd::socket::init
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
2017-09-12 18:28:41 +02:00
|
|
|
std::unique_ptr<ip::tcp::resolver> resolver;
|
|
|
|
|
2016-11-29 16:23:38 +01:00
|
|
|
init();
|
|
|
|
~init() noexcept;
|
|
|
|
};
|
|
|
|
|
|
|
|
template<class iov>
|
|
|
|
auto
|
2017-08-28 23:51:22 +02:00
|
|
|
ircd::socket::write(const iov &bufs)
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
2017-03-13 22:07:58 +01:00
|
|
|
return io(*this, out, [&]
|
|
|
|
{
|
2017-09-21 04:31:54 +02:00
|
|
|
return async_write(ssl, bufs, asio::transfer_all(), yield_context{to_asio{}});
|
2017-03-13 22:07:58 +01:00
|
|
|
});
|
2016-11-29 16:23:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class iov>
|
|
|
|
auto
|
2017-08-28 23:51:22 +02:00
|
|
|
ircd::socket::write(const iov &bufs,
|
|
|
|
error_code &ec)
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
2017-03-13 22:07:58 +01:00
|
|
|
return io(*this, out, [&]
|
|
|
|
{
|
2017-09-21 04:31:54 +02:00
|
|
|
return async_write(ssl, bufs, asio::transfer_all(), yield_context{to_asio{}}[ec]);
|
2017-03-13 22:07:58 +01:00
|
|
|
});
|
2016-11-29 16:23:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class iov>
|
|
|
|
auto
|
2017-08-28 23:51:22 +02:00
|
|
|
ircd::socket::write_some(const iov &bufs)
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
2017-03-13 22:07:58 +01:00
|
|
|
return io(*this, out, [&]
|
|
|
|
{
|
2017-09-21 04:31:54 +02:00
|
|
|
return ssl.async_write_some(bufs, yield_context{to_asio{}});
|
2017-03-13 22:07:58 +01:00
|
|
|
});
|
2016-11-29 16:23:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class iov>
|
|
|
|
auto
|
2017-08-28 23:51:22 +02:00
|
|
|
ircd::socket::write_some(const iov &bufs,
|
|
|
|
error_code &ec)
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
2017-03-13 22:07:58 +01:00
|
|
|
return io(*this, out, [&]
|
|
|
|
{
|
2017-09-21 04:31:54 +02:00
|
|
|
return ssl.async_write_some(bufs, yield_context{to_asio{}}[ec]);
|
2017-03-13 22:07:58 +01:00
|
|
|
});
|
2016-11-29 16:23:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
template<class iov>
|
|
|
|
auto
|
2017-08-28 23:51:22 +02:00
|
|
|
ircd::socket::read(const iov &bufs)
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
|
|
|
return io(*this, in, [&]
|
|
|
|
{
|
2017-09-21 04:31:54 +02:00
|
|
|
const size_t ret(async_read(ssl, bufs, yield_context{to_asio{}}));
|
2016-11-29 16:23:38 +01:00
|
|
|
|
|
|
|
if(unlikely(!ret))
|
|
|
|
throw boost::system::system_error(boost::asio::error::eof);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class iov>
|
|
|
|
auto
|
2017-08-28 23:51:22 +02:00
|
|
|
ircd::socket::read(const iov &bufs,
|
|
|
|
error_code &ec)
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
|
|
|
return io(*this, in, [&]
|
|
|
|
{
|
2017-09-21 04:31:54 +02:00
|
|
|
return async_read(ssl, bufs, yield_context{to_asio{}}[ec]);
|
2016-11-29 16:23:38 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class iov>
|
|
|
|
auto
|
2017-08-28 23:51:22 +02:00
|
|
|
ircd::socket::read_some(const iov &bufs)
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
|
|
|
return io(*this, in, [&]
|
|
|
|
{
|
2017-09-21 04:31:54 +02:00
|
|
|
const size_t ret(ssl.async_read_some(bufs, yield_context{to_asio{}}));
|
2016-11-29 16:23:38 +01:00
|
|
|
|
|
|
|
if(unlikely(!ret))
|
|
|
|
throw boost::system::system_error(boost::asio::error::eof);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class iov>
|
|
|
|
auto
|
2017-08-28 23:51:22 +02:00
|
|
|
ircd::socket::read_some(const iov &bufs,
|
|
|
|
error_code &ec)
|
2016-11-29 16:23:38 +01:00
|
|
|
{
|
|
|
|
return io(*this, in, [&]
|
|
|
|
{
|
2017-09-21 04:31:54 +02:00
|
|
|
return ssl.async_read_some(bufs, yield_context{to_asio{}}[ec]);
|
2016-11-29 16:23:38 +01:00
|
|
|
});
|
|
|
|
}
|