mirror of
https://github.com/matrix-construct/construct
synced 2024-12-26 07:23:53 +01:00
ircd::net: Refine network subsystem; break up interfaces; various.
This commit is contained in:
parent
7e0c01708a
commit
ac3802c0f9
10 changed files with 1050 additions and 635 deletions
51
include/ircd/net/README.md
Normal file
51
include/ircd/net/README.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
## IRCd Networking
|
||||
|
||||
#### open()
|
||||
|
||||
The open() call combines DNS resolution, TCP connecting and SSL handshaking.
|
||||
A dedicated options structure is provided to tweak the specifics of the process.
|
||||
|
||||
#### close()
|
||||
|
||||
Like the open() sequence, the close() sequence composes a complex of
|
||||
multiple operations to close_notify the SSL session with cryptographic
|
||||
soundness and then disconnect the TCP session. A dedicated options
|
||||
structure is provided to tweak details.
|
||||
|
||||
#### read()
|
||||
|
||||
To keep things simple, this system has no notion of non-blocking or even
|
||||
asynchronous reads. In other words, you call read() when you know there
|
||||
is something to be read(). If there is nothing your ircd::ctx will yield
|
||||
until the call is interrupted/canceled by some timeout etc (which is
|
||||
something you might want too in certain contexts).
|
||||
|
||||
There are two possibilities: either the remote is faster than us or they
|
||||
are slower than us.
|
||||
|
||||
* If the remote is faster than us, or faster than we want: we'll know data
|
||||
is available but ignore it to slow them down (by not calling read()) --
|
||||
a matter in which we may not have a choice anyway.
|
||||
|
||||
* If the remote is slower than us: you have to use a timer to put a limit
|
||||
on how much time (and resource) is going to be spent on receiving their
|
||||
data; eventually the daemon has to move on to serving other clients.
|
||||
|
||||
#### write()
|
||||
|
||||
This system uses two different techniques for sending data to remotes
|
||||
intended for two different categories of transmission:
|
||||
|
||||
* write_all() is an intuitive synchronous send which yields the ircd::ctx
|
||||
until all bytes have been written to the remote. This is intended for
|
||||
serving simple requests or most other ctx-per-client models. A coarse timer
|
||||
is generally used to prevent the remote from being too slow to receive but
|
||||
we have some tolerance to wait a little for them.
|
||||
|
||||
* write_any()/write_one() is a non-blocking send intended for small message
|
||||
mass-push models or some other custom flow control on larger messages. The
|
||||
goal here is to figure out what to do when the socket buffer has filled
|
||||
up because the remote has been too slow to receive data. The choice is
|
||||
usually to either propagate the slowdown to the source or to drop the
|
||||
remote so the daemon can move on without using up memory.
|
||||
|
75
include/ircd/net/close.h
Normal file
75
include/ircd/net/close.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Charybdis Development Team
|
||||
* Copyright (C) 2017 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
|
||||
#define HAVE_IRCD_NET_CLOSE_H
|
||||
|
||||
namespace ircd::net
|
||||
{
|
||||
enum class dc;
|
||||
struct close_opts extern const close_opts_default;
|
||||
using close_callback = std::function<void (std::exception_ptr)>;
|
||||
|
||||
// Callback-based closer.
|
||||
void close(socket &, const close_opts &, close_callback);
|
||||
|
||||
// Future-based closer.
|
||||
ctx::future<void> close(socket &, const close_opts & = close_opts_default);
|
||||
|
||||
// Fire-and-forget helper callback for close().
|
||||
extern const close_callback close_ignore;
|
||||
}
|
||||
|
||||
/// Types of disconnection. SSL_NOTIFY is the recommended type of
|
||||
/// disconnection and is usually default. RST is an immediate
|
||||
/// alternative which has no asynchronous operations.
|
||||
enum class ircd::net::dc
|
||||
{
|
||||
RST, ///< hardest immediate termination
|
||||
FIN, ///< sd graceful shutdown both directions
|
||||
FIN_SEND, ///< sd graceful shutdown send side
|
||||
FIN_RECV, ///< sd graceful shutdown recv side
|
||||
SSL_NOTIFY, ///< SSL close_notify
|
||||
};
|
||||
|
||||
/// Close options structure.
|
||||
struct ircd::net::close_opts
|
||||
{
|
||||
close_opts() = default;
|
||||
close_opts(const net::dc &);
|
||||
|
||||
/// The type of close() to be conducted is specified here.
|
||||
net::dc type { dc::SSL_NOTIFY };
|
||||
|
||||
/// The coarse duration allowed for the close() process.
|
||||
milliseconds timeout { 5000ms };
|
||||
|
||||
/// If specified, these socket options will be applied when conducting
|
||||
/// the disconnect (useful for adding an SO_LINGER time etc).
|
||||
const sockopts *sopts { nullptr };
|
||||
};
|
||||
|
||||
/// Allows for implicit construction of close_opts in arguments to close()
|
||||
/// without requiring brackets for the close_opts& argument.
|
||||
inline
|
||||
ircd::net::close_opts::close_opts(const net::dc &type)
|
||||
:type{type}
|
||||
{}
|
|
@ -40,6 +40,7 @@ namespace ircd::net
|
|||
IRCD_EXCEPTION(error, inauthentic)
|
||||
|
||||
struct init;
|
||||
struct socket;
|
||||
|
||||
// SNOMASK 'N' "net"
|
||||
extern struct log::log log;
|
||||
|
@ -48,7 +49,27 @@ namespace ircd::net
|
|||
#include "remote.h"
|
||||
#include "resolve.h"
|
||||
#include "listener.h"
|
||||
#include "sockpub.h"
|
||||
#include "sockopts.h"
|
||||
#include "open.h"
|
||||
#include "close.h"
|
||||
#include "read.h"
|
||||
#include "write.h"
|
||||
|
||||
namespace ircd::net
|
||||
{
|
||||
bool connected(const socket &) noexcept;
|
||||
size_t readable(const socket &);
|
||||
size_t available(const socket &) noexcept;
|
||||
ipport local_ipport(const socket &) noexcept;
|
||||
ipport remote_ipport(const socket &) noexcept;
|
||||
const_raw_buffer peer_cert_der(const mutable_raw_buffer &, const socket &);
|
||||
}
|
||||
|
||||
// Exports to ircd::
|
||||
namespace ircd
|
||||
{
|
||||
using net::socket;
|
||||
}
|
||||
|
||||
struct ircd::net::init
|
||||
{
|
||||
|
|
117
include/ircd/net/open.h
Normal file
117
include/ircd/net/open.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Charybdis Development Team
|
||||
* Copyright (C) 2017 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
|
||||
#define HAVE_IRCD_NET_OPEN_H
|
||||
|
||||
namespace ircd::net
|
||||
{
|
||||
struct open_opts;
|
||||
using open_callback = std::function<void (std::exception_ptr)>;
|
||||
|
||||
// Open existing socket with callback.
|
||||
void open(socket &, const open_opts &, open_callback);
|
||||
|
||||
// Open new socket with callback.
|
||||
std::shared_ptr<socket> open(const open_opts &, open_callback);
|
||||
|
||||
// Open new socket with future.
|
||||
ctx::future<std::shared_ptr<socket>> open(const open_opts &);
|
||||
}
|
||||
|
||||
/// Connection options structure. This is provided when making a client
|
||||
/// connection with a socket. Unless otherwise noted it usually has to
|
||||
/// remain in scope as a const reference for the duration of that process.
|
||||
/// Some of its members are also thin and will have to remain in scope along
|
||||
/// with it.
|
||||
struct ircd::net::open_opts
|
||||
{
|
||||
open_opts() = default;
|
||||
open_opts(const net::ipport &ipport);
|
||||
open_opts(const net::hostport &hostport);
|
||||
explicit open_opts(const net::remote &remote);
|
||||
|
||||
/// Remote's hostname and port. This will be used for address resolution
|
||||
/// if an ipport is not also provided later. The hostname will also be used
|
||||
/// for certificate /CN verification if common_name is not provided later.
|
||||
net::hostport hostport;
|
||||
|
||||
/// Remote's resolved IP and port. Providing this skips DNS resolution if
|
||||
/// hostport is not given; required if so.
|
||||
net::ipport ipport;
|
||||
|
||||
/// The duration allowed for the TCP connection.
|
||||
milliseconds connect_timeout { 8000ms };
|
||||
|
||||
/// Pointer to a sockopts structure which will be applied to this socket
|
||||
/// if given. Defaults to null; no application is made.
|
||||
const sockopts *sopts { nullptr };
|
||||
|
||||
/// Option to toggle whether to perform the SSL handshake; you want true.
|
||||
bool handshake { true };
|
||||
|
||||
/// The duration allowed for the SSL handshake
|
||||
milliseconds handshake_timeout { 8000ms };
|
||||
|
||||
/// Option to toggle whether to perform any certificate verification; if
|
||||
/// false, everything no matter what is considered valid; you want true.
|
||||
bool verify_certificate { true };
|
||||
|
||||
/// Option to toggle whether to perform CN verification to ensure the
|
||||
/// certificate is signed to the actual host we want to talk to. When
|
||||
/// true, see the comments for `common_name`. Otherwise if false, any
|
||||
/// common_name will pass muster.
|
||||
bool verify_common_name { true };
|
||||
|
||||
/// The expected /CN of the target. This should be the remote's hostname,
|
||||
/// If it is empty then `hostport.host` is used. If the signed /CN has
|
||||
/// some rfc2818/rfc2459 wildcard we will properly match that for you.
|
||||
string_view common_name;
|
||||
|
||||
/// Option to toggle whether to allow self-signed certificates. This
|
||||
/// currently defaults to true to not break Matrix development but will
|
||||
/// likely change later and require setting to true for specific conns.
|
||||
bool allow_self_signed { true };
|
||||
|
||||
/// Option to toggle whether to allow self-signed certificate authorities
|
||||
/// in the chain. This is what corporate network nanny's may use to spy.
|
||||
bool allow_self_chain { false };
|
||||
};
|
||||
|
||||
/// Constructor intended to provide implicit conversions (no-brackets required)
|
||||
/// in arguments to open(); i.e open(hostport) rather than open({hostport});
|
||||
inline
|
||||
ircd::net::open_opts::open_opts(const net::hostport &hostport)
|
||||
:hostport{hostport}
|
||||
{}
|
||||
|
||||
/// Constructor intended to provide implicit conversions (no-brackets required)
|
||||
/// in arguments to open(); i.e open(ipport) rather than open({ipport});
|
||||
inline
|
||||
ircd::net::open_opts::open_opts(const net::ipport &ipport)
|
||||
:ipport{ipport}
|
||||
{}
|
||||
|
||||
inline
|
||||
ircd::net::open_opts::open_opts(const net::remote &remote)
|
||||
:hostport{remote.hostname}
|
||||
,ipport{remote}
|
||||
{}
|
78
include/ircd/net/read.h
Normal file
78
include/ircd/net/read.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Charybdis Development Team
|
||||
* Copyright (C) 2017 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
|
||||
#define HAVE_IRCD_NET_READ_H
|
||||
|
||||
namespace ircd::net
|
||||
{
|
||||
// Yields until something is read into buffers.
|
||||
size_t read_any(socket &, const vector_view<const mutable_buffer> &);
|
||||
size_t read_any(socket &, const mutable_buffer &);
|
||||
|
||||
// Yields until buffers are entirely full.
|
||||
size_t read_all(socket &, const vector_view<const mutable_buffer> &);
|
||||
size_t read_all(socket &, const mutable_buffer &);
|
||||
|
||||
// Alias to read_any();
|
||||
size_t read(socket &, const vector_view<const mutable_buffer> &);
|
||||
size_t read(socket &, const mutable_buffer &);
|
||||
}
|
||||
|
||||
/// Alias to read_any();
|
||||
inline size_t
|
||||
ircd::net::read(socket &socket,
|
||||
const mutable_buffer &buffer)
|
||||
{
|
||||
return read_any(socket, buffer);
|
||||
}
|
||||
|
||||
/// Alias to read_any();
|
||||
inline size_t
|
||||
ircd::net::read(socket &socket,
|
||||
const vector_view<const mutable_buffer> &buffers)
|
||||
{
|
||||
return read_any(socket, buffers);
|
||||
}
|
||||
|
||||
inline size_t
|
||||
ircd::net::read_all(socket &socket,
|
||||
const mutable_buffer &buffer)
|
||||
{
|
||||
const mutable_buffer buffers[]
|
||||
{
|
||||
buffer
|
||||
};
|
||||
|
||||
return read_all(socket, buffers);
|
||||
}
|
||||
|
||||
inline size_t
|
||||
ircd::net::read_any(socket &socket,
|
||||
const mutable_buffer &buffer)
|
||||
{
|
||||
const mutable_buffer buffers[]
|
||||
{
|
||||
buffer
|
||||
};
|
||||
|
||||
return read_any(socket, buffers);
|
||||
}
|
|
@ -48,8 +48,8 @@ struct ircd::net::socket
|
|||
using wait_type = ip::tcp::socket::wait_type;
|
||||
using message_flags = 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>;
|
||||
using xfer_handler = std::function<void (xfer) noexcept>;
|
||||
using ec_handler = std::function<void (const error_code &) noexcept>;
|
||||
using eptr_handler = std::function<void (std::exception_ptr) noexcept>;
|
||||
|
||||
struct stat
|
||||
{
|
||||
|
@ -63,12 +63,14 @@ struct ircd::net::socket
|
|||
stat in, out;
|
||||
bool timedout {false};
|
||||
|
||||
void call_user(const handler &, const error_code &ec) noexcept;
|
||||
bool handle_verify(bool, asio::ssl::verify_context &, const connopts &) noexcept;
|
||||
void handle_handshake(std::weak_ptr<socket> wp, const connopts &, handler, const error_code &ec) noexcept;
|
||||
void handle_connect(std::weak_ptr<socket> wp, const connopts &, handler, const error_code &ec) noexcept;
|
||||
void handle_timeout(std::weak_ptr<socket> wp, const error_code &ec) noexcept;
|
||||
void handle(std::weak_ptr<socket>, handler, const error_code &) noexcept;
|
||||
void call_user(const eptr_handler &, const error_code &) noexcept;
|
||||
void call_user(const ec_handler &, const error_code &) noexcept;
|
||||
bool handle_verify(bool, asio::ssl::verify_context &, const open_opts &) noexcept;
|
||||
void handle_disconnect(std::shared_ptr<socket>, eptr_handler, const error_code &) noexcept;
|
||||
void handle_handshake(std::weak_ptr<socket>, const open_opts &, eptr_handler, const error_code &) noexcept;
|
||||
void handle_connect(std::weak_ptr<socket>, const open_opts &, eptr_handler, const error_code &) noexcept;
|
||||
void handle_timeout(std::weak_ptr<socket>, const error_code &) noexcept;
|
||||
void handle(std::weak_ptr<socket>, ec_handler, const error_code &) noexcept;
|
||||
|
||||
public:
|
||||
operator const ip::tcp::socket &() const { return sd; }
|
||||
|
@ -79,33 +81,29 @@ struct ircd::net::socket
|
|||
endpoint remote() const; // getpeername(); throws if not conn
|
||||
endpoint local() const; // getsockname(); throws if not conn/bound
|
||||
|
||||
// low level read suite
|
||||
template<class iov> auto read_some(const iov &, xfer_handler);
|
||||
template<class iov> auto read_some(const iov &);
|
||||
template<class iov> auto read(const iov &, xfer_handler);
|
||||
template<class iov> auto read(const iov &);
|
||||
|
||||
// low level write suite
|
||||
template<class iov> auto write_some(const iov &, xfer_handler);
|
||||
template<class iov> auto write_some(const iov &);
|
||||
template<class iov> auto write(const iov &, xfer_handler);
|
||||
template<class iov> auto write(const iov &);
|
||||
|
||||
// Timer for this socket
|
||||
bool has_timeout() const noexcept;
|
||||
void set_timeout(const milliseconds &, handler);
|
||||
void set_timeout(const milliseconds &, ec_handler);
|
||||
void set_timeout(const milliseconds &);
|
||||
milliseconds cancel_timeout() noexcept;
|
||||
|
||||
// low level write suite
|
||||
template<class iov> size_t write_one(iov&&);
|
||||
template<class iov> size_t write_any(iov&&);
|
||||
template<class iov> size_t write_all(iov&&);
|
||||
|
||||
// low level read suite
|
||||
template<class iov> size_t read_any(iov&&);
|
||||
template<class iov> size_t read_all(iov&&);
|
||||
|
||||
// Asynchronous callback when socket ready
|
||||
void operator()(const wait_type &, const milliseconds &timeout, handler);
|
||||
void operator()(const wait_type &, handler);
|
||||
void operator()(const wait_type &, const milliseconds &timeout, ec_handler);
|
||||
void operator()(const wait_type &, ec_handler);
|
||||
bool cancel() noexcept;
|
||||
|
||||
void handshake(const connopts &, handler callback);
|
||||
void connect(const endpoint &, const connopts &, handler callback);
|
||||
|
||||
bool disconnect(const dc &type);
|
||||
void disconnect(const close_opts &, eptr_handler);
|
||||
void handshake(const open_opts &, eptr_handler);
|
||||
void connect(const endpoint &, const open_opts &, eptr_handler);
|
||||
|
||||
socket(asio::ssl::context &ssl = sslv23_client,
|
||||
boost::asio::io_service *const &ios = ircd::ios);
|
||||
|
@ -124,7 +122,7 @@ class ircd::net::socket::scope_timeout
|
|||
bool cancel() noexcept; // invoke timer.cancel() before dtor
|
||||
bool release(); // cancels the cancel;
|
||||
|
||||
scope_timeout(socket &, const milliseconds &timeout, socket::handler handler);
|
||||
scope_timeout(socket &, const milliseconds &timeout, socket::ec_handler handler);
|
||||
scope_timeout(socket &, const milliseconds &timeout);
|
||||
scope_timeout() = default;
|
||||
scope_timeout(scope_timeout &&) noexcept;
|
||||
|
@ -134,135 +132,106 @@ class ircd::net::socket::scope_timeout
|
|||
~scope_timeout() noexcept;
|
||||
};
|
||||
|
||||
class ircd::net::socket::io
|
||||
{
|
||||
struct socket &sock;
|
||||
struct stat &stat;
|
||||
size_t bytes;
|
||||
|
||||
public:
|
||||
operator size_t() const;
|
||||
|
||||
io(struct socket &, struct stat &, const size_t &bytes);
|
||||
io(struct socket &, struct stat &, const std::function<size_t ()> &closure);
|
||||
};
|
||||
|
||||
struct ircd::net::socket::xfer
|
||||
{
|
||||
std::exception_ptr eptr;
|
||||
size_t bytes {0};
|
||||
|
||||
xfer(const error_code &ec = {}, const size_t &bytes = 0);
|
||||
};
|
||||
|
||||
/// Yields ircd::ctx until buffers are full.
|
||||
template<class iov>
|
||||
auto
|
||||
ircd::net::socket::write(const iov &bufs)
|
||||
size_t
|
||||
ircd::net::socket::read_all(iov&& bufs)
|
||||
{
|
||||
return io{*this, out, [&]
|
||||
static const auto completion
|
||||
{
|
||||
return async_write(ssl, bufs, asio::transfer_all(), yield_context{to_asio{}});
|
||||
}};
|
||||
}
|
||||
asio::transfer_all()
|
||||
};
|
||||
|
||||
template<class iov>
|
||||
auto
|
||||
ircd::net::socket::write(const iov &bufs,
|
||||
xfer_handler handler)
|
||||
{
|
||||
async_write(ssl, bufs, asio::transfer_all(), [this, handler(std::move(handler))]
|
||||
(const error_code &ec, const size_t &bytes)
|
||||
noexcept
|
||||
const size_t ret
|
||||
{
|
||||
io{*this, out, bytes};
|
||||
handler(xfer{ec, bytes});
|
||||
});
|
||||
}
|
||||
asio::async_read(ssl, std::forward<iov>(bufs), completion, yield_context{to_asio{}})
|
||||
};
|
||||
|
||||
template<class iov>
|
||||
auto
|
||||
ircd::net::socket::write_some(const iov &bufs)
|
||||
{
|
||||
return io{*this, out, [&]
|
||||
{
|
||||
return ssl.async_write_some(bufs, yield_context{to_asio{}});
|
||||
}};
|
||||
}
|
||||
|
||||
template<class iov>
|
||||
auto
|
||||
ircd::net::socket::write_some(const iov &bufs,
|
||||
xfer_handler handler)
|
||||
{
|
||||
ssl.async_write_some(bufs, [this, handler(std::move(handler))]
|
||||
(const error_code &ec, const size_t &bytes)
|
||||
noexcept
|
||||
{
|
||||
io{*this, out, bytes};
|
||||
handler(xfer{ec, bytes});
|
||||
});
|
||||
}
|
||||
|
||||
template<class iov>
|
||||
auto
|
||||
ircd::net::socket::read(const iov &bufs)
|
||||
{
|
||||
return io{*this, in, [&]
|
||||
{
|
||||
const size_t ret
|
||||
if(!ret)
|
||||
throw boost::system::system_error
|
||||
{
|
||||
async_read(ssl, bufs, yield_context{to_asio{}})
|
||||
boost::asio::error::eof
|
||||
};
|
||||
|
||||
if(unlikely(!ret))
|
||||
throw boost::system::system_error(boost::asio::error::eof);
|
||||
|
||||
return ret;
|
||||
}};
|
||||
in.bytes += ret;
|
||||
++in.calls;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Yields ircd::ctx until remote has sent at least some data.
|
||||
template<class iov>
|
||||
auto
|
||||
ircd::net::socket::read(const iov &bufs,
|
||||
xfer_handler handler)
|
||||
size_t
|
||||
ircd::net::socket::read_any(iov&& bufs)
|
||||
{
|
||||
async_read(ssl, bufs, [this, handler(std::move(handler))]
|
||||
(const error_code &ec, const size_t &bytes)
|
||||
noexcept
|
||||
const size_t ret
|
||||
{
|
||||
io{*this, in, bytes};
|
||||
handler(xfer{ec, bytes});
|
||||
});
|
||||
}
|
||||
ssl.async_read_some(std::forward<iov>(bufs), yield_context{to_asio{}})
|
||||
};
|
||||
|
||||
template<class iov>
|
||||
auto
|
||||
ircd::net::socket::read_some(const iov &bufs)
|
||||
{
|
||||
return io{*this, in, [&]
|
||||
{
|
||||
const size_t ret
|
||||
if(!ret)
|
||||
throw boost::system::system_error
|
||||
{
|
||||
ssl.async_read_some(bufs, yield_context{to_asio{}})
|
||||
boost::asio::error::eof
|
||||
};
|
||||
|
||||
if(unlikely(!ret))
|
||||
throw boost::system::system_error(boost::asio::error::eof);
|
||||
|
||||
return ret;
|
||||
}};
|
||||
in.bytes += ret;
|
||||
++in.calls;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Yields ircd::ctx until all buffers are sent.
|
||||
template<class iov>
|
||||
auto
|
||||
ircd::net::socket::read_some(const iov &bufs,
|
||||
xfer_handler handler)
|
||||
size_t
|
||||
ircd::net::socket::write_all(iov&& bufs)
|
||||
{
|
||||
ssl.async_read_some(bufs, [this, handler(std::move(handler))]
|
||||
(const error_code &ec, const size_t &bytes)
|
||||
noexcept
|
||||
static const auto completion
|
||||
{
|
||||
io{*this, in, bytes};
|
||||
handler(xfer{ec, bytes});
|
||||
});
|
||||
asio::transfer_all()
|
||||
};
|
||||
|
||||
const size_t ret
|
||||
{
|
||||
asio::async_write(ssl, std::forward<iov>(bufs), completion, yield_context{to_asio{}})
|
||||
};
|
||||
|
||||
out.bytes += ret;
|
||||
++out.calls;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Non-blocking; writes as much as possible by with multiple write_one()'s
|
||||
template<class iov>
|
||||
size_t
|
||||
ircd::net::socket::write_any(iov&& bufs)
|
||||
{
|
||||
static const auto completion
|
||||
{
|
||||
asio::transfer_all()
|
||||
};
|
||||
|
||||
assert(!blocking(*this));
|
||||
const size_t ret
|
||||
{
|
||||
asio::write(ssl, std::forward<iov>(bufs), completion)
|
||||
};
|
||||
|
||||
out.bytes += ret;
|
||||
++out.calls;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Non-blocking; Writes one "unit" of data or less; never more.
|
||||
template<class iov>
|
||||
size_t
|
||||
ircd::net::socket::write_one(iov&& bufs)
|
||||
{
|
||||
assert(!blocking(*this));
|
||||
const size_t ret
|
||||
{
|
||||
ssl.write_some(std::forward<iov>(bufs))
|
||||
};
|
||||
|
||||
out.bytes += ret;
|
||||
++out.calls;
|
||||
return ret;
|
||||
}
|
||||
|
|
70
include/ircd/net/sockopts.h
Normal file
70
include/ircd/net/sockopts.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Charybdis Development Team
|
||||
* Copyright (C) 2017 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
|
||||
#define HAVE_IRCD_NET_SOCKOPTS_H
|
||||
|
||||
namespace ircd::net
|
||||
{
|
||||
struct sockopts;
|
||||
|
||||
bool blocking(const socket &);
|
||||
bool nodelay(const socket &);
|
||||
bool keepalive(const socket &);
|
||||
time_t linger(const socket &);
|
||||
size_t read_bufsz(const socket &);
|
||||
size_t write_bufsz(const socket &);
|
||||
size_t read_lowat(const socket &);
|
||||
size_t write_lowat(const socket &);
|
||||
|
||||
void blocking(socket &, const bool &);
|
||||
void nodelay(socket &, const bool &);
|
||||
void keepalive(socket &, const bool &);
|
||||
void linger(socket &, const time_t &); // -1 is OFF; >= 0 is ON
|
||||
void read_bufsz(socket &, const size_t &bytes);
|
||||
void write_bufsz(socket &, const size_t &bytes);
|
||||
void read_lowat(socket &, const size_t &bytes);
|
||||
void write_lowat(socket &, const size_t &bytes);
|
||||
|
||||
void set(socket &, const sockopts &);
|
||||
}
|
||||
|
||||
/// Socket options convenience aggregate. This structure allows observation
|
||||
/// or manipulation of socket options all together. Pass an active socket to
|
||||
/// the constructor to observe all options. Use net::set(socket, sockopts) to
|
||||
/// set all non-ignored options.
|
||||
struct ircd::net::sockopts
|
||||
{
|
||||
/// Magic value to not set this option on a set() pass.
|
||||
static constexpr int8_t IGN { std::numeric_limits<int8_t>::min() };
|
||||
|
||||
int8_t blocking { IGN }; // Simulates blocking behavior
|
||||
int8_t nodelay { IGN };
|
||||
int8_t keepalive { IGN };
|
||||
time_t linger { IGN }; // -1 is OFF; >= 0 is ON
|
||||
ssize_t read_bufsz { IGN };
|
||||
ssize_t write_bufsz { IGN };
|
||||
ssize_t read_lowat { IGN };
|
||||
ssize_t write_lowat { IGN };
|
||||
|
||||
sockopts(const socket &); // Get options from socket
|
||||
sockopts() = default;
|
||||
};
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Charybdis Development Team
|
||||
* Copyright (C) 2017 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
|
||||
#define HAVE_IRCD_NET_SOCKPUB_H
|
||||
|
||||
// This is the public interface to net::socket because socket.h is not part of
|
||||
// the standard include group as it directly involves boost headers. For
|
||||
// direct access you may include <ircd/asio.h> in your definition file if
|
||||
// absolutely necessary.
|
||||
//
|
||||
// Any operation on the socket can trigger a pending error (i.e disconnection
|
||||
// userspace doesn't know about yet) and thus make any call after related to
|
||||
// the socket invalid and throw. We use noexcept here when there is a
|
||||
// reasonable default value returned instead of throwing. The goal there is to
|
||||
// reduce the number of places where the stack can blow up: for example, a
|
||||
// debug log call that prints the bytes available for reading. During testing
|
||||
// that may be the place of first observation where an exception keeps getting
|
||||
// thrown but during release that call won't be there and thus lays the
|
||||
// foundation for surprise heisenbugs.
|
||||
|
||||
namespace ircd::net
|
||||
{
|
||||
struct socket;
|
||||
struct sockopts;
|
||||
struct connopts;
|
||||
enum class dc;
|
||||
}
|
||||
|
||||
namespace ircd
|
||||
{
|
||||
using net::socket;
|
||||
}
|
||||
|
||||
namespace ircd::net
|
||||
{
|
||||
bool connected(const socket &) noexcept;
|
||||
size_t readable(const socket &);
|
||||
size_t available(const socket &) noexcept;
|
||||
ipport local_ipport(const socket &) noexcept;
|
||||
ipport remote_ipport(const socket &) noexcept;
|
||||
const_raw_buffer peer_cert_der(const mutable_raw_buffer &, const socket &);
|
||||
|
||||
size_t read(socket &, const ilist<mutable_buffer> &); // read_all
|
||||
size_t read(socket &, const iov<mutable_buffer> &); // read_all
|
||||
size_t read(socket &, iov<mutable_buffer> &); // read_some
|
||||
|
||||
size_t write(socket &, const ilist<const_buffer> &); // write_all
|
||||
size_t write(socket &, const iov<const_buffer> &); // write_all
|
||||
size_t write(socket &, iov<const_buffer> &); // write_some
|
||||
void flush(socket &);
|
||||
|
||||
bool disconnect(socket &, const dc &type) noexcept;
|
||||
bool disconnect(socket &) noexcept;
|
||||
|
||||
void open(socket &, const connopts &, std::function<void (std::exception_ptr)>);
|
||||
ctx::future<std::shared_ptr<socket>> open(const connopts &);
|
||||
}
|
||||
|
||||
/// Connection options structure. This is provided when making a client
|
||||
/// connection with a socket. Unless otherwise noted it usually has to
|
||||
/// remain in scope as a const reference for the duration of that process.
|
||||
/// Some of its members are also thin and will have to remain in scope along
|
||||
/// with it.
|
||||
struct ircd::net::connopts
|
||||
{
|
||||
/// Remote's hostname and port. This will be used for address resolution
|
||||
/// if an ipport is not also provided later. The hostname will also be used
|
||||
/// for certificate /CN verification if common_name is not provided later.
|
||||
net::hostport hostport;
|
||||
|
||||
/// Remote's resolved IP and port. Providing this skips DNS resolution if
|
||||
/// hostport is not given; required if so.
|
||||
net::ipport ipport;
|
||||
|
||||
/// The duration allowed for the TCP connection.
|
||||
milliseconds connect_timeout { 8000ms };
|
||||
|
||||
/// Pointer to a sockopts structure which will be applied to this socket
|
||||
/// if given. Defaults to null; no application is made.
|
||||
const sockopts *sopts { nullptr };
|
||||
|
||||
/// Option to toggle whether to perform the SSL handshake; you want true.
|
||||
bool handshake { true };
|
||||
|
||||
/// The duration allowed for the SSL handshake
|
||||
milliseconds handshake_timeout { 8000ms };
|
||||
|
||||
/// Option to toggle whether to perform any certificate verification; if
|
||||
/// false, everything no matter what is considered valid; you want true.
|
||||
bool verify_certificate { true };
|
||||
|
||||
/// Option to toggle whether to perform CN verification to ensure the
|
||||
/// certificate is signed to the actual host we want to talk to. When
|
||||
/// true, see the comments for `common_name`. Otherwise if false, any
|
||||
/// common_name will pass muster.
|
||||
bool verify_common_name { true };
|
||||
|
||||
/// The expected /CN of the target. This should be the remote's hostname,
|
||||
/// If it is empty then `hostport.host` is used. If the signed /CN has
|
||||
/// some rfc2818/rfc2459 wildcard we will properly match that for you.
|
||||
string_view common_name;
|
||||
|
||||
/// Option to toggle whether to allow self-signed certificates. This
|
||||
/// currently defaults to true to not break Matrix development but will
|
||||
/// likely change later and require setting to true for specific conns.
|
||||
bool allow_self_signed { true };
|
||||
|
||||
/// Option to toggle whether to allow self-signed certificate authorities
|
||||
/// in the chain. This is what corporate network nanny's may use to spy.
|
||||
bool allow_self_chain { false };
|
||||
};
|
||||
|
||||
// Socket options section
|
||||
namespace ircd::net
|
||||
{
|
||||
bool blocking(const socket &);
|
||||
bool nodelay(const socket &);
|
||||
bool keepalive(const socket &);
|
||||
time_t linger(const socket &);
|
||||
size_t read_bufsz(const socket &);
|
||||
size_t write_bufsz(const socket &);
|
||||
size_t read_lowat(const socket &);
|
||||
size_t write_lowat(const socket &);
|
||||
|
||||
void blocking(socket &, const bool &);
|
||||
void nodelay(socket &, const bool &);
|
||||
void keepalive(socket &, const bool &);
|
||||
void linger(socket &, const time_t &); // -1 is OFF; >= 0 is ON
|
||||
void read_bufsz(socket &, const size_t &bytes);
|
||||
void write_bufsz(socket &, const size_t &bytes);
|
||||
void read_lowat(socket &, const size_t &bytes);
|
||||
void write_lowat(socket &, const size_t &bytes);
|
||||
|
||||
void set(socket &, const sockopts &);
|
||||
}
|
||||
|
||||
/// Socket options convenience aggregate. This structure allows observation
|
||||
/// or manipulation of socket options all together. Pass an active socket to
|
||||
/// the constructor to observe all options. Use net::set(socket, sockopts) to
|
||||
/// set all non-ignored options.
|
||||
struct ircd::net::sockopts
|
||||
{
|
||||
/// Magic value to not set this option on a set() pass.
|
||||
static constexpr int8_t IGN { std::numeric_limits<int8_t>::min() };
|
||||
|
||||
int8_t blocking = IGN; // Simulates blocking behavior
|
||||
int8_t nodelay = IGN;
|
||||
int8_t keepalive = IGN;
|
||||
time_t linger = IGN; // -1 is OFF; >= 0 is ON
|
||||
ssize_t read_bufsz = IGN;
|
||||
ssize_t write_bufsz = IGN;
|
||||
ssize_t read_lowat = IGN;
|
||||
ssize_t write_lowat = IGN;
|
||||
|
||||
sockopts(const socket &); // Get options from socket
|
||||
sockopts() = default;
|
||||
};
|
||||
|
||||
/// Arguments for disconnecting.
|
||||
enum class ircd::net::dc
|
||||
{
|
||||
RST, ///< hardest immediate termination
|
||||
FIN, ///< sd graceful shutdown both directions
|
||||
FIN_SEND, ///< sd graceful shutdown send side
|
||||
FIN_RECV, ///< sd graceful shutdown recv side
|
||||
SSL_NOTIFY, ///< SSL close_notify (async, errors ignored)
|
||||
SSL_NOTIFY_YIELD, ///< SSL close_notify (yields context, throws)
|
||||
};
|
100
include/ircd/net/write.h
Normal file
100
include/ircd/net/write.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Charybdis Development Team
|
||||
* Copyright (C) 2017 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
|
||||
#define HAVE_IRCD_NET_WRITE_H
|
||||
|
||||
namespace ircd::net
|
||||
{
|
||||
// Non-blocking; writes at most one system-determined amount of
|
||||
// bytes or less with at most a single syscall.
|
||||
size_t write_one(socket &, const vector_view<const const_buffer> &);
|
||||
size_t write_one(socket &, const const_buffer &);
|
||||
|
||||
// Non-blocking; writes as much as possible until the socket buffer
|
||||
// is full. This composes multiple write_one()'s.
|
||||
size_t write_any(socket &, const vector_view<const const_buffer> &);
|
||||
size_t write_any(socket &, const const_buffer &);
|
||||
|
||||
// Blocking; Yields your ircd::ctx until all bytes have been written;
|
||||
// advise one uses a timeout in conjunction to prevent DoS.
|
||||
size_t write_all(socket &, const vector_view<const const_buffer> &);
|
||||
size_t write_all(socket &, const const_buffer &);
|
||||
|
||||
// Alias to write_all();
|
||||
size_t write(socket &, const vector_view<const const_buffer> &);
|
||||
size_t write(socket &, const const_buffer &);
|
||||
|
||||
// Toggles nodelay to force a transmission.
|
||||
void flush(socket &);
|
||||
}
|
||||
|
||||
/// Alias to write_all();
|
||||
inline size_t
|
||||
ircd::net::write(socket &socket,
|
||||
const const_buffer &buffer)
|
||||
{
|
||||
return write_all(socket, buffer);
|
||||
}
|
||||
|
||||
/// Alias to write_all();
|
||||
inline size_t
|
||||
ircd::net::write(socket &socket,
|
||||
const vector_view<const const_buffer> &buffers)
|
||||
{
|
||||
return write_all(socket, buffers);
|
||||
}
|
||||
|
||||
inline size_t
|
||||
ircd::net::write_all(socket &socket,
|
||||
const const_buffer &buffer)
|
||||
{
|
||||
const const_buffer buffers[]
|
||||
{
|
||||
buffer
|
||||
};
|
||||
|
||||
return write_all(socket, buffers);
|
||||
}
|
||||
|
||||
inline size_t
|
||||
ircd::net::write_any(socket &socket,
|
||||
const const_buffer &buffer)
|
||||
{
|
||||
const const_buffer buffers[]
|
||||
{
|
||||
buffer
|
||||
};
|
||||
|
||||
return write_all(socket, buffers);
|
||||
}
|
||||
|
||||
inline size_t
|
||||
ircd::net::write_one(socket &socket,
|
||||
const const_buffer &buffer)
|
||||
{
|
||||
const const_buffer buffers[]
|
||||
{
|
||||
buffer
|
||||
};
|
||||
|
||||
return write_one(socket, buffers);
|
||||
}
|
743
ircd/net.cc
743
ircd/net.cc
|
@ -59,153 +59,6 @@ ircd::net::init::~init()
|
|||
resolve::resolver.reset(nullptr);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// net/sockpub.h
|
||||
//
|
||||
|
||||
bool
|
||||
ircd::net::disconnect(socket &socket)
|
||||
noexcept
|
||||
{
|
||||
return disconnect(socket, dc::SSL_NOTIFY);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::net::disconnect(socket &socket,
|
||||
const dc &type)
|
||||
noexcept try
|
||||
{
|
||||
socket.disconnect(type);
|
||||
return true;
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
/*
|
||||
log::error("socket(%p): disconnect: type: %d: %s",
|
||||
this,
|
||||
int(type),
|
||||
e.what());
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Attempt to connect and ssl handshake; future.
|
||||
///
|
||||
ircd::ctx::future<std::shared_ptr<ircd::net::socket>>
|
||||
ircd::net::open(const connopts &opts)
|
||||
{
|
||||
ctx::promise<std::shared_ptr<socket>> p;
|
||||
ctx::future<std::shared_ptr<socket>> f(p);
|
||||
auto s{std::make_shared<socket>()};
|
||||
open(*s, opts, [s, p(std::move(p))]
|
||||
(std::exception_ptr eptr)
|
||||
mutable
|
||||
{
|
||||
if(eptr)
|
||||
p.set_exception(std::move(eptr));
|
||||
else
|
||||
p.set_value(s);
|
||||
});
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/// Attempt to connect and ssl handshake; asynchronous, callback when done.
|
||||
///
|
||||
void
|
||||
ircd::net::open(socket &socket,
|
||||
const connopts &opts,
|
||||
std::function<void (std::exception_ptr)> handler)
|
||||
{
|
||||
auto complete{[&socket, &opts, handler(std::move(handler))]
|
||||
(std::exception_ptr eptr)
|
||||
{
|
||||
if(eptr)
|
||||
disconnect(socket, dc::RST);
|
||||
|
||||
handler(std::move(eptr));
|
||||
}};
|
||||
|
||||
auto connector{[&socket, &opts, complete(std::move(complete))]
|
||||
(std::exception_ptr eptr, const ipport &ipport)
|
||||
{
|
||||
if(eptr)
|
||||
return complete(std::move(eptr));
|
||||
|
||||
const auto ep{make_endpoint(ipport)};
|
||||
socket.connect(ep, opts, [&socket, complete(std::move(complete))]
|
||||
(const error_code &ec)
|
||||
{
|
||||
complete(make_eptr(ec));
|
||||
});
|
||||
}};
|
||||
|
||||
if(!opts.ipport)
|
||||
resolve(opts.hostport, std::move(connector));
|
||||
else
|
||||
connector({}, opts.ipport);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::net::flush(socket &socket)
|
||||
{
|
||||
if(nodelay(socket))
|
||||
return;
|
||||
|
||||
nodelay(socket, true);
|
||||
nodelay(socket, false);
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::net::write(socket &socket,
|
||||
iov<const_buffer> &bufs)
|
||||
{
|
||||
const size_t wrote(socket.write_some(bufs));
|
||||
const size_t consumed(consume(bufs, wrote));
|
||||
assert(wrote == consumed);
|
||||
return consumed;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::net::write(socket &socket,
|
||||
const iov<const_buffer> &bufs)
|
||||
{
|
||||
const size_t wrote(socket.write(bufs));
|
||||
assert(wrote == size(bufs));
|
||||
return wrote;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::net::write(socket &socket,
|
||||
const ilist<const_buffer> &bufs)
|
||||
{
|
||||
const size_t wrote(socket.write(bufs));
|
||||
assert(wrote == size(bufs));
|
||||
return wrote;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::net::read(socket &socket,
|
||||
iov<mutable_buffer> &bufs)
|
||||
{
|
||||
const size_t read(socket.read_some(bufs));
|
||||
const size_t consumed(buffer::consume(bufs, read));
|
||||
assert(read == consumed);
|
||||
return read;
|
||||
}
|
||||
|
||||
size_t
|
||||
ircd::net::read(socket &socket,
|
||||
const iov<mutable_buffer> &bufs)
|
||||
{
|
||||
return socket.read(bufs);
|
||||
}
|
||||
|
||||
//
|
||||
// Active observers
|
||||
//
|
||||
|
||||
ircd::const_raw_buffer
|
||||
ircd::net::peer_cert_der(const mutable_raw_buffer &buf,
|
||||
const socket &socket)
|
||||
|
@ -269,8 +122,239 @@ catch(...)
|
|||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Options
|
||||
// net/write.h
|
||||
//
|
||||
|
||||
void
|
||||
ircd::net::flush(socket &socket)
|
||||
{
|
||||
if(nodelay(socket))
|
||||
return;
|
||||
|
||||
nodelay(socket, true);
|
||||
nodelay(socket, false);
|
||||
}
|
||||
|
||||
/// Yields ircd::ctx until all buffers are sent.
|
||||
///
|
||||
/// This is blocking behavior; use this if the following are true:
|
||||
///
|
||||
/// * You put a timer on the socket so if the remote slows us down the data
|
||||
/// will not occupy the daemon's memory for a long time. Remember, *all* of
|
||||
/// the data will be sitting in memory even after some of it was ack'ed by
|
||||
/// the remote.
|
||||
///
|
||||
/// * You are willing to dedicate the ircd::ctx to sending all the data to
|
||||
/// the remote. The ircd::ctx will be yielding until everything is sent.
|
||||
///
|
||||
size_t
|
||||
ircd::net::write_all(socket &socket,
|
||||
const vector_view<const const_buffer> &buffers)
|
||||
{
|
||||
return socket.write_all(buffers);
|
||||
}
|
||||
|
||||
/// Writes as much as possible until one of the following is true:
|
||||
///
|
||||
/// * The kernel buffer for the socket is full.
|
||||
/// * The user buffer is exhausted.
|
||||
///
|
||||
/// This is non-blocking behavior. No yielding will take place; no timer is
|
||||
/// needed. Multiple syscalls will be composed to fulfill the above points.
|
||||
///
|
||||
size_t
|
||||
ircd::net::write_any(socket &socket,
|
||||
const vector_view<const const_buffer> &buffers)
|
||||
{
|
||||
return socket.write_any(buffers);
|
||||
}
|
||||
|
||||
/// Writes one "unit" of data or less; never more. The size of that unit
|
||||
/// is determined by the system. Less may be written if one of the following
|
||||
/// is true:
|
||||
///
|
||||
/// * The kernel buffer for the socket is full.
|
||||
/// * The user buffer is exhausted.
|
||||
///
|
||||
/// If neither are true, more can be written using additional calls;
|
||||
/// alternatively, use other variants of write_ for that.
|
||||
///
|
||||
/// This is non-blocking behavior. No yielding will take place; no timer is
|
||||
/// needed. Only one syscall will occur.
|
||||
///
|
||||
size_t
|
||||
ircd::net::write_one(socket &socket,
|
||||
const vector_view<const const_buffer> &buffers)
|
||||
{
|
||||
return socket.write_one(buffers);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// net/read.h
|
||||
//
|
||||
|
||||
/// Yields ircd::ctx until buffers are full.
|
||||
///
|
||||
/// Use this only if the following are true:
|
||||
///
|
||||
/// * You know the remote has made a guarantee to send you a specific amount
|
||||
/// of data.
|
||||
///
|
||||
/// * You put a timer on the socket so that if the remote runs short this
|
||||
/// call doesn't hang the ircd::ctx forever, otherwise it will until cancel.
|
||||
///
|
||||
/// * You are willing to dedicate the ircd::ctx to just this operation for
|
||||
/// that amount of time.
|
||||
///
|
||||
size_t
|
||||
ircd::net::read_all(socket &socket,
|
||||
const vector_view<const mutable_buffer> &buffers)
|
||||
{
|
||||
return socket.read_all(buffers);
|
||||
}
|
||||
|
||||
/// Yields ircd::ctx until remote has sent at least one frame. The buffers may
|
||||
/// be filled with any amount of data depending on what has accumulated.
|
||||
///
|
||||
/// Use this if the following are true:
|
||||
///
|
||||
/// * You know there is data to be read; you can do this asynchronously with
|
||||
/// other features of the socket. Otherwise this will hang the ircd::ctx.
|
||||
///
|
||||
/// * You are willing to dedicate the ircd::ctx to just this operation,
|
||||
/// which is non-blocking if data is known to be available, but may be
|
||||
/// blocking if this call is made in the blind.
|
||||
///
|
||||
size_t
|
||||
ircd::net::read_any(socket &socket,
|
||||
const vector_view<const mutable_buffer> &buffers)
|
||||
{
|
||||
return socket.read_any(buffers);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// net/close.h
|
||||
//
|
||||
|
||||
/// Static instance of default close options.
|
||||
ircd::net::close_opts
|
||||
const ircd::net::close_opts_default
|
||||
{
|
||||
};
|
||||
|
||||
/// Static helper callback which may be passed to the callback-based overload
|
||||
/// of close(). This callback does nothing.
|
||||
ircd::net::close_callback
|
||||
const ircd::net::close_ignore{[]
|
||||
(std::exception_ptr eptr)
|
||||
{
|
||||
return;
|
||||
}};
|
||||
|
||||
ircd::ctx::future<void>
|
||||
ircd::net::close(socket &socket,
|
||||
const close_opts &opts)
|
||||
{
|
||||
ctx::promise<void> p;
|
||||
ctx::future<void> f(p);
|
||||
close(socket, opts, [p(std::move(p))]
|
||||
(std::exception_ptr eptr)
|
||||
mutable
|
||||
{
|
||||
if(eptr)
|
||||
p.set_exception(std::move(eptr));
|
||||
else
|
||||
p.set_value();
|
||||
});
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
void
|
||||
ircd::net::close(socket &socket,
|
||||
const close_opts &opts,
|
||||
close_callback callback)
|
||||
{
|
||||
socket.disconnect(opts, std::move(callback));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// net/open.h
|
||||
//
|
||||
|
||||
/// Open new socket with future-based report.
|
||||
///
|
||||
ircd::ctx::future<std::shared_ptr<ircd::net::socket>>
|
||||
ircd::net::open(const open_opts &opts)
|
||||
{
|
||||
ctx::promise<std::shared_ptr<socket>> p;
|
||||
ctx::future<std::shared_ptr<socket>> f(p);
|
||||
auto s{std::make_shared<socket>()};
|
||||
open(*s, opts, [s, p(std::move(p))]
|
||||
(std::exception_ptr eptr)
|
||||
mutable
|
||||
{
|
||||
if(eptr)
|
||||
p.set_exception(std::move(eptr));
|
||||
else
|
||||
p.set_value(s);
|
||||
});
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
/// Open existing socket with callback-based report.
|
||||
///
|
||||
std::shared_ptr<ircd::net::socket>
|
||||
ircd::net::open(const open_opts &opts,
|
||||
open_callback handler)
|
||||
{
|
||||
auto s{std::make_shared<socket>()};
|
||||
open(*s, opts, std::move(handler));
|
||||
return s;
|
||||
}
|
||||
|
||||
/// Open existing socket with callback-based report.
|
||||
///
|
||||
void
|
||||
ircd::net::open(socket &socket,
|
||||
const open_opts &opts,
|
||||
open_callback handler)
|
||||
{
|
||||
auto complete{[s(shared_from(socket)), &opts, handler(std::move(handler))]
|
||||
(std::exception_ptr eptr)
|
||||
{
|
||||
if(eptr)
|
||||
close(*s, dc::RST);
|
||||
|
||||
handler(std::move(eptr));
|
||||
}};
|
||||
|
||||
auto connector{[&socket, &opts, complete(std::move(complete))]
|
||||
(std::exception_ptr eptr, const ipport &ipport)
|
||||
{
|
||||
if(eptr)
|
||||
return complete(std::move(eptr));
|
||||
|
||||
const auto ep{make_endpoint(ipport)};
|
||||
socket.connect(ep, opts, std::move(complete));
|
||||
}};
|
||||
|
||||
if(!opts.ipport)
|
||||
resolve(opts.hostport, std::move(connector));
|
||||
else
|
||||
connector({}, opts.ipport);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// net/sopts.h
|
||||
//
|
||||
|
||||
/// Construct sockopts with the current options from socket argument
|
||||
|
@ -407,6 +491,30 @@ ircd::net::nodelay(socket &socket,
|
|||
sd.set_option(option);
|
||||
}
|
||||
|
||||
/// Toggles the behavior of non-async asio calls.
|
||||
///
|
||||
/// This option affects very little in practice and only sets a flag in
|
||||
/// userspace in asio, not an actual ioctl(). Specifically:
|
||||
///
|
||||
/// * All sockets are already set by asio to FIONBIO=1 no matter what, thus
|
||||
/// nothing really blocks the event loop ever by default unless you try hard.
|
||||
///
|
||||
/// * All asio::async_ and sd.async_ and ssl.async_ calls will always do what
|
||||
/// the synchronous/blocking alternative would have accomplished but using
|
||||
/// the async methodology. i.e if a buffer is full you will always wait
|
||||
/// asynchronously: async_write() will wait for everything, async_write_some()
|
||||
/// will wait for something, etc -- but there will never be true non-blocking
|
||||
/// _effective behavior_ from these calls.
|
||||
///
|
||||
/// * All asio non-async calls conduct blocking by (on linux) poll()'ing the
|
||||
/// socket to get a real kernel-blocking operation out of it (this is the
|
||||
/// try-hard part).
|
||||
///
|
||||
/// This flag only controls the behavior of the last bullet. In practice,
|
||||
/// in this project there is never a reason to ever set this to true,
|
||||
/// however, sockets do get constructed by asio in blocking mode by default
|
||||
/// so we mostly use this function to set it to non-blocking.
|
||||
///
|
||||
void
|
||||
ircd::net::blocking(socket &socket,
|
||||
const bool &b)
|
||||
|
@ -511,11 +619,11 @@ struct ircd::net::listener::acceptor
|
|||
void configure(const json::object &opts);
|
||||
|
||||
// Handshake stack
|
||||
bool handshake_error(const error_code &ec, socket &);
|
||||
void check_handshake_error(const error_code &ec, socket &);
|
||||
void handshake(const error_code &ec, std::shared_ptr<socket>, std::weak_ptr<acceptor>) noexcept;
|
||||
|
||||
// Acceptance stack
|
||||
bool accept_error(const error_code &ec, socket &);
|
||||
void check_accept_error(const error_code &ec, socket &);
|
||||
void accept(const error_code &ec, std::shared_ptr<socket>, std::weak_ptr<acceptor>) noexcept;
|
||||
|
||||
// Accept next
|
||||
|
@ -716,21 +824,19 @@ noexcept try
|
|||
const unwind::exceptional drop{[&sock]
|
||||
{
|
||||
assert(bool(sock));
|
||||
disconnect(*sock, dc::RST);
|
||||
close(*sock, dc::RST, close_ignore);
|
||||
}};
|
||||
|
||||
assert(bool(sock));
|
||||
if(unlikely(accept_error(ec, *sock)))
|
||||
{
|
||||
disconnect(*sock, dc::RST);
|
||||
return;
|
||||
}
|
||||
|
||||
check_accept_error(ec, *sock);
|
||||
log.debug("%s: socket(%p) accepted %s",
|
||||
std::string(*this),
|
||||
sock.get(),
|
||||
string(sock->remote()));
|
||||
|
||||
// Toggles the behavior of non-async functions; see func comment
|
||||
blocking(*sock, false);
|
||||
|
||||
static const socket::handshake_type handshake_type
|
||||
{
|
||||
socket::handshake_type::server
|
||||
|
@ -766,9 +872,9 @@ catch(const std::exception &e)
|
|||
/// whether or not the handler should return or continue processing the
|
||||
/// result.
|
||||
///
|
||||
bool
|
||||
ircd::net::listener::acceptor::accept_error(const error_code &ec,
|
||||
socket &sock)
|
||||
void
|
||||
ircd::net::listener::acceptor::check_accept_error(const error_code &ec,
|
||||
socket &sock)
|
||||
{
|
||||
using namespace boost::system::errc;
|
||||
using boost::system::system_category;
|
||||
|
@ -777,12 +883,12 @@ ircd::net::listener::acceptor::accept_error(const error_code &ec,
|
|||
throw ctx::interrupted();
|
||||
|
||||
if(likely(ec == success))
|
||||
return false;
|
||||
return;
|
||||
|
||||
if(ec.category() == system_category()) switch(ec.value())
|
||||
{
|
||||
case operation_canceled:
|
||||
return false;
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -801,18 +907,14 @@ noexcept try
|
|||
return;
|
||||
|
||||
--handshaking;
|
||||
assert(bool(sock));
|
||||
const unwind::exceptional drop{[&sock]
|
||||
{
|
||||
disconnect(*sock, dc::RST);
|
||||
if(bool(sock))
|
||||
close(*sock, dc::RST, close_ignore);
|
||||
}};
|
||||
|
||||
if(unlikely(handshake_error(ec, *sock)))
|
||||
{
|
||||
disconnect(*sock, dc::RST);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(bool(sock));
|
||||
check_handshake_error(ec, *sock);
|
||||
log.debug("%s socket(%p): SSL handshook %s",
|
||||
std::string(*this),
|
||||
sock.get(),
|
||||
|
@ -842,9 +944,9 @@ catch(const std::exception &e)
|
|||
/// whether or not the handler should return or continue processing the
|
||||
/// result.
|
||||
///
|
||||
bool
|
||||
ircd::net::listener::acceptor::handshake_error(const error_code &ec,
|
||||
socket &sock)
|
||||
void
|
||||
ircd::net::listener::acceptor::check_handshake_error(const error_code &ec,
|
||||
socket &sock)
|
||||
{
|
||||
using boost::system::system_category;
|
||||
using namespace boost::system::errc;
|
||||
|
@ -853,12 +955,12 @@ ircd::net::listener::acceptor::handshake_error(const error_code &ec,
|
|||
throw ctx::interrupted();
|
||||
|
||||
if(likely(ec == success))
|
||||
return false;
|
||||
return;
|
||||
|
||||
if(ec.category() == system_category()) switch(ec.value())
|
||||
{
|
||||
case operation_canceled:
|
||||
return false;
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -1032,8 +1134,8 @@ catch(const std::exception &e)
|
|||
|
||||
void
|
||||
ircd::net::socket::connect(const endpoint &ep,
|
||||
const connopts &opts,
|
||||
handler callback)
|
||||
const open_opts &opts,
|
||||
eptr_handler callback)
|
||||
{
|
||||
log.debug("socket(%p) attempting connect to remote: %s for the next %ld$ms",
|
||||
this,
|
||||
|
@ -1050,8 +1152,8 @@ ircd::net::socket::connect(const endpoint &ep,
|
|||
}
|
||||
|
||||
void
|
||||
ircd::net::socket::handshake(const connopts &opts,
|
||||
handler callback)
|
||||
ircd::net::socket::handshake(const open_opts &opts,
|
||||
eptr_handler callback)
|
||||
{
|
||||
log.debug("socket(%p) performing handshake with %s for '%s' for the next %ld$ms",
|
||||
this,
|
||||
|
@ -1074,101 +1176,72 @@ ircd::net::socket::handshake(const connopts &opts,
|
|||
set_timeout(opts.handshake_timeout);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::net::socket::disconnect(const dc &type)
|
||||
void
|
||||
ircd::net::socket::disconnect(const close_opts &opts,
|
||||
eptr_handler callback)
|
||||
try
|
||||
{
|
||||
if(timer.expires_from_now() > 0ms)
|
||||
timer.cancel();
|
||||
|
||||
if(sd.is_open())
|
||||
log.debug("socket(%p): disconnect: %s type:%d user: in:%zu out:%zu",
|
||||
(const void *)this,
|
||||
ircd::string(remote_ipport(*this)),
|
||||
uint(type),
|
||||
in.bytes,
|
||||
out.bytes);
|
||||
|
||||
if(sd.is_open()) switch(type)
|
||||
if(!sd.is_open())
|
||||
{
|
||||
call_user(callback, {});
|
||||
return;
|
||||
}
|
||||
|
||||
const bool cancelation{cancel()};
|
||||
log.debug("socket(%p): disconnect: %s type:%d user: in:%zu out:%zu cancel:%d",
|
||||
(const void *)this,
|
||||
ircd::string(remote_ipport(*this)),
|
||||
uint(opts.type),
|
||||
in.bytes,
|
||||
out.bytes,
|
||||
cancelation);
|
||||
|
||||
if(opts.sopts)
|
||||
set(*this, *opts.sopts);
|
||||
|
||||
switch(opts.type)
|
||||
{
|
||||
default:
|
||||
case dc::RST:
|
||||
sd.close();
|
||||
return true;
|
||||
break;
|
||||
|
||||
case dc::FIN:
|
||||
sd.shutdown(ip::tcp::socket::shutdown_both);
|
||||
return true;
|
||||
break;
|
||||
|
||||
case dc::FIN_SEND:
|
||||
sd.shutdown(ip::tcp::socket::shutdown_send);
|
||||
return true;
|
||||
break;
|
||||
|
||||
case dc::FIN_RECV:
|
||||
sd.shutdown(ip::tcp::socket::shutdown_receive);
|
||||
return true;
|
||||
|
||||
case dc::SSL_NOTIFY_YIELD: if(likely(ctx::current))
|
||||
{
|
||||
const life_guard<socket> lg{*this};
|
||||
const scope_timeout ts{*this, 8s};
|
||||
ssl.async_shutdown(yield_context{to_asio{}});
|
||||
error_code ec;
|
||||
sd.close(ec);
|
||||
if(ec)
|
||||
log.error("socket(%p): close: %s: %s",
|
||||
this,
|
||||
string(ec));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case dc::SSL_NOTIFY:
|
||||
{
|
||||
ssl.async_shutdown([s(shared_from_this())]
|
||||
(error_code ec)
|
||||
noexcept
|
||||
auto disconnect_handler
|
||||
{
|
||||
if(!s->timedout)
|
||||
s->cancel_timeout();
|
||||
std::bind(&socket::handle_disconnect, this, shared_from(*this), std::move(callback), ph::_1)
|
||||
};
|
||||
|
||||
if(ec)
|
||||
log.warning("socket(%p): SSL_NOTIFY: %s: %s",
|
||||
s.get(),
|
||||
string(ec));
|
||||
|
||||
if(!s->sd.is_open())
|
||||
return;
|
||||
|
||||
s->sd.close(ec);
|
||||
|
||||
if(ec)
|
||||
log.warning("socket(%p): after SSL_NOTIFY: %s: %s",
|
||||
s.get(),
|
||||
string(ec));
|
||||
});
|
||||
set_timeout(8s);
|
||||
return true;
|
||||
ssl.async_shutdown(std::move(disconnect_handler));
|
||||
set_timeout(opts.timeout);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
|
||||
call_user(callback, {});
|
||||
}
|
||||
catch(const boost::system::system_error &e)
|
||||
{
|
||||
log.warning("socket(%p): disconnect: type: %d: %s",
|
||||
(const void *)this,
|
||||
uint(type),
|
||||
e.what());
|
||||
|
||||
if(sd.is_open())
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
sd.close(ec);
|
||||
if(ec)
|
||||
log.warning("socket(%p): after disconnect: %s: %s",
|
||||
this,
|
||||
string(ec));
|
||||
}
|
||||
|
||||
call_user(callback, e.code());
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log.critical("socket(%p): disconnect: type: %d: %s",
|
||||
(const void *)this,
|
||||
uint(opts.type),
|
||||
e.what());
|
||||
throw;
|
||||
}
|
||||
|
||||
|
@ -1193,7 +1266,7 @@ noexcept
|
|||
///
|
||||
void
|
||||
ircd::net::socket::operator()(const wait_type &type,
|
||||
handler h)
|
||||
ec_handler h)
|
||||
{
|
||||
operator()(type, milliseconds(-1), std::move(h));
|
||||
}
|
||||
|
@ -1206,7 +1279,7 @@ ircd::net::socket::operator()(const wait_type &type,
|
|||
void
|
||||
ircd::net::socket::operator()(const wait_type &type,
|
||||
const milliseconds &timeout,
|
||||
handler callback)
|
||||
ec_handler callback)
|
||||
{
|
||||
auto handle
|
||||
{
|
||||
|
@ -1242,7 +1315,7 @@ ircd::net::socket::operator()(const wait_type &type,
|
|||
|
||||
void
|
||||
ircd::net::socket::handle(const std::weak_ptr<socket> wp,
|
||||
const handler callback,
|
||||
const ec_handler callback,
|
||||
const error_code &ec)
|
||||
noexcept try
|
||||
{
|
||||
|
@ -1279,6 +1352,15 @@ noexcept try
|
|||
|
||||
call_user(callback, ec);
|
||||
}
|
||||
catch(const boost::system::system_error &e)
|
||||
{
|
||||
log.error("socket(%p): handle: %s",
|
||||
this,
|
||||
e.what());
|
||||
|
||||
assert(0);
|
||||
call_user(callback, e.code());
|
||||
}
|
||||
catch(const std::bad_weak_ptr &e)
|
||||
{
|
||||
// This handler may still be registered with asio after the socket destructs, so
|
||||
|
@ -1287,14 +1369,18 @@ catch(const std::bad_weak_ptr &e)
|
|||
log.warning("socket(%p): belated callback to handler... (%s)",
|
||||
this,
|
||||
e.what());
|
||||
|
||||
assert(0);
|
||||
call_user(callback, ec);
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log.critical("socket(%p): handle: %s",
|
||||
this,
|
||||
e.what());
|
||||
|
||||
assert(0);
|
||||
call_user(callback, ec);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1345,8 +1431,8 @@ catch(const std::exception &e)
|
|||
|
||||
void
|
||||
ircd::net::socket::handle_connect(std::weak_ptr<socket> wp,
|
||||
const connopts &opts,
|
||||
handler callback,
|
||||
const open_opts &opts,
|
||||
eptr_handler callback,
|
||||
const error_code &ec)
|
||||
noexcept try
|
||||
{
|
||||
|
@ -1366,16 +1452,13 @@ noexcept try
|
|||
if(ec)
|
||||
return call_user(callback, ec);
|
||||
|
||||
// Try to set the user's socket options now; if something fails
|
||||
// we can invoke their callback with the error.
|
||||
if(opts.sopts) try
|
||||
{
|
||||
// Toggles the behavior of non-async functions; see func comment
|
||||
blocking(*this, false);
|
||||
|
||||
// Try to set the user's socket options now; if something fails we can
|
||||
// invoke their callback with the error from the exception handler.
|
||||
if(opts.sopts)
|
||||
set(*this, *opts.sopts);
|
||||
}
|
||||
catch(const boost::system::system_error &e)
|
||||
{
|
||||
return call_user(callback, e.code());
|
||||
}
|
||||
|
||||
// The user can opt out of performing the handshake here.
|
||||
if(!opts.handshake)
|
||||
|
@ -1388,20 +1471,74 @@ catch(const std::bad_weak_ptr &e)
|
|||
log.warning("socket(%p): belated callback to handle_connect... (%s)",
|
||||
this,
|
||||
e.what());
|
||||
|
||||
assert(0);
|
||||
call_user(callback, ec);
|
||||
}
|
||||
catch(const boost::system::system_error &e)
|
||||
{
|
||||
log.error("socket(%p): after connect: %s",
|
||||
this,
|
||||
e.what());
|
||||
|
||||
assert(0);
|
||||
call_user(callback, e.code());
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log.critical("socket(%p): handle_connect: %s",
|
||||
this,
|
||||
e.what());
|
||||
|
||||
assert(0);
|
||||
call_user(callback, ec);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::net::socket::handle_disconnect(std::shared_ptr<socket> s,
|
||||
eptr_handler callback,
|
||||
const error_code &ec)
|
||||
noexcept try
|
||||
{
|
||||
assert(!timedout || ec == boost::system::errc::operation_canceled);
|
||||
log.debug("socket(%p) disconnect from local: %s to remote: %s: %s",
|
||||
this,
|
||||
string(local_ipport(*this)),
|
||||
string(remote_ipport(*this)),
|
||||
string(ec));
|
||||
|
||||
// The timer was set by socket::disconnect() and may need to be canceled.
|
||||
if(!timedout)
|
||||
cancel_timeout();
|
||||
|
||||
if(unlikely(sd.is_open()))
|
||||
sd.close();
|
||||
|
||||
call_user(callback, ec);
|
||||
}
|
||||
catch(const boost::system::system_error &e)
|
||||
{
|
||||
log.error("socket(%p): disconnect: %s",
|
||||
this,
|
||||
e.what());
|
||||
|
||||
assert(0);
|
||||
call_user(callback, e.code());
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log.critical("socket(%p): disconnect: %s",
|
||||
this,
|
||||
e.what());
|
||||
|
||||
assert(0);
|
||||
call_user(callback, ec);
|
||||
}
|
||||
|
||||
void
|
||||
ircd::net::socket::handle_handshake(std::weak_ptr<socket> wp,
|
||||
const connopts &opts,
|
||||
handler callback,
|
||||
const open_opts &opts,
|
||||
eptr_handler callback,
|
||||
const error_code &ec)
|
||||
noexcept try
|
||||
{
|
||||
|
@ -1421,12 +1558,22 @@ noexcept try
|
|||
// back with or without error here.
|
||||
call_user(callback, ec);
|
||||
}
|
||||
catch(const boost::system::system_error &e)
|
||||
{
|
||||
log.error("socket(%p): after handshake: %s",
|
||||
this,
|
||||
e.what());
|
||||
|
||||
assert(0);
|
||||
call_user(callback, e.code());
|
||||
}
|
||||
catch(const std::bad_weak_ptr &e)
|
||||
{
|
||||
log.warning("socket(%p): belated callback to handle_handshake... (%s)",
|
||||
this,
|
||||
e.what());
|
||||
assert(0);
|
||||
call_user(callback, ec);
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
|
@ -1434,12 +1581,13 @@ catch(const std::exception &e)
|
|||
this,
|
||||
e.what());
|
||||
assert(0);
|
||||
call_user(callback, ec);
|
||||
}
|
||||
|
||||
bool
|
||||
ircd::net::socket::handle_verify(const bool valid,
|
||||
asio::ssl::verify_context &vc,
|
||||
const connopts &opts)
|
||||
const open_opts &opts)
|
||||
noexcept try
|
||||
{
|
||||
// `valid` indicates whether or not there's an anomaly with the
|
||||
|
@ -1546,7 +1694,7 @@ catch(const std::exception &e)
|
|||
}
|
||||
|
||||
void
|
||||
ircd::net::socket::call_user(const handler &callback,
|
||||
ircd::net::socket::call_user(const ec_handler &callback,
|
||||
const error_code &ec)
|
||||
noexcept try
|
||||
{
|
||||
|
@ -1559,6 +1707,20 @@ catch(const std::exception &e)
|
|||
e.what());
|
||||
}
|
||||
|
||||
void
|
||||
ircd::net::socket::call_user(const eptr_handler &callback,
|
||||
const error_code &ec)
|
||||
noexcept try
|
||||
{
|
||||
callback(make_eptr(ec));
|
||||
}
|
||||
catch(const std::exception &e)
|
||||
{
|
||||
log.critical("socket(%p): async handler: unhandled exception: %s",
|
||||
this,
|
||||
e.what());
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::endpoint
|
||||
ircd::net::socket::local()
|
||||
const
|
||||
|
@ -1602,7 +1764,7 @@ ircd::net::socket::set_timeout(const milliseconds &t)
|
|||
|
||||
void
|
||||
ircd::net::socket::set_timeout(const milliseconds &t,
|
||||
handler h)
|
||||
ec_handler h)
|
||||
{
|
||||
cancel_timeout();
|
||||
if(t < milliseconds(0))
|
||||
|
@ -1636,36 +1798,6 @@ const
|
|||
return *ssl.native_handle();
|
||||
}
|
||||
|
||||
//
|
||||
// socket::io
|
||||
//
|
||||
|
||||
ircd::net::socket::io::io(struct socket &sock,
|
||||
struct stat &stat,
|
||||
const std::function<size_t ()> &closure)
|
||||
:io
|
||||
{
|
||||
sock, stat, closure()
|
||||
}
|
||||
{}
|
||||
|
||||
ircd::net::socket::io::io(struct socket &sock,
|
||||
struct stat &stat,
|
||||
const size_t &bytes)
|
||||
:sock{sock}
|
||||
,stat{stat}
|
||||
,bytes{bytes}
|
||||
{
|
||||
stat.bytes += bytes;
|
||||
stat.calls++;
|
||||
}
|
||||
|
||||
ircd::net::socket::io::operator size_t()
|
||||
const
|
||||
{
|
||||
return bytes;
|
||||
}
|
||||
|
||||
//
|
||||
// socket::scope_timeout
|
||||
//
|
||||
|
@ -1679,7 +1811,7 @@ ircd::net::socket::scope_timeout::scope_timeout(socket &socket,
|
|||
|
||||
ircd::net::socket::scope_timeout::scope_timeout(socket &socket,
|
||||
const milliseconds &timeout,
|
||||
socket::handler handler)
|
||||
socket::ec_handler handler)
|
||||
:s{&socket}
|
||||
{
|
||||
socket.set_timeout(timeout, std::move(handler));
|
||||
|
@ -1736,17 +1868,6 @@ ircd::net::socket::scope_timeout::release()
|
|||
return s != nullptr;
|
||||
}
|
||||
|
||||
//
|
||||
// socket::xfer
|
||||
//
|
||||
|
||||
ircd::net::socket::xfer::xfer(const error_code &error_code,
|
||||
const size_t &bytes)
|
||||
:eptr{make_eptr(error_code)}
|
||||
,bytes{bytes}
|
||||
{
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// net/resolve.h
|
||||
|
|
Loading…
Reference in a new issue