mirror of
https://github.com/matrix-construct/construct
synced 2024-12-26 15:33:54 +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)
|
IRCD_EXCEPTION(error, inauthentic)
|
||||||
|
|
||||||
struct init;
|
struct init;
|
||||||
|
struct socket;
|
||||||
|
|
||||||
// SNOMASK 'N' "net"
|
// SNOMASK 'N' "net"
|
||||||
extern struct log::log log;
|
extern struct log::log log;
|
||||||
|
@ -48,7 +49,27 @@ namespace ircd::net
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
#include "resolve.h"
|
#include "resolve.h"
|
||||||
#include "listener.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
|
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 wait_type = ip::tcp::socket::wait_type;
|
||||||
using message_flags = asio::socket_base::message_flags;
|
using message_flags = asio::socket_base::message_flags;
|
||||||
using handshake_type = asio::ssl::stream<ip::tcp::socket>::handshake_type;
|
using handshake_type = asio::ssl::stream<ip::tcp::socket>::handshake_type;
|
||||||
using handler = std::function<void (const error_code &) noexcept>;
|
using ec_handler = std::function<void (const error_code &) noexcept>;
|
||||||
using xfer_handler = std::function<void (xfer) noexcept>;
|
using eptr_handler = std::function<void (std::exception_ptr) noexcept>;
|
||||||
|
|
||||||
struct stat
|
struct stat
|
||||||
{
|
{
|
||||||
|
@ -63,12 +63,14 @@ struct ircd::net::socket
|
||||||
stat in, out;
|
stat in, out;
|
||||||
bool timedout {false};
|
bool timedout {false};
|
||||||
|
|
||||||
void call_user(const handler &, const error_code &ec) noexcept;
|
void call_user(const eptr_handler &, const error_code &) noexcept;
|
||||||
bool handle_verify(bool, asio::ssl::verify_context &, const connopts &) noexcept;
|
void call_user(const ec_handler &, const error_code &) noexcept;
|
||||||
void handle_handshake(std::weak_ptr<socket> wp, const connopts &, handler, const error_code &ec) noexcept;
|
bool handle_verify(bool, asio::ssl::verify_context &, const open_opts &) noexcept;
|
||||||
void handle_connect(std::weak_ptr<socket> wp, const connopts &, handler, const error_code &ec) noexcept;
|
void handle_disconnect(std::shared_ptr<socket>, eptr_handler, const error_code &) noexcept;
|
||||||
void handle_timeout(std::weak_ptr<socket> wp, const error_code &ec) noexcept;
|
void handle_handshake(std::weak_ptr<socket>, const open_opts &, eptr_handler, const error_code &) noexcept;
|
||||||
void handle(std::weak_ptr<socket>, 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:
|
public:
|
||||||
operator const ip::tcp::socket &() const { return sd; }
|
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 remote() const; // getpeername(); throws if not conn
|
||||||
endpoint local() const; // getsockname(); throws if not conn/bound
|
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
|
// Timer for this socket
|
||||||
bool has_timeout() const noexcept;
|
bool has_timeout() const noexcept;
|
||||||
void set_timeout(const milliseconds &, handler);
|
void set_timeout(const milliseconds &, ec_handler);
|
||||||
void set_timeout(const milliseconds &);
|
void set_timeout(const milliseconds &);
|
||||||
milliseconds cancel_timeout() noexcept;
|
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
|
// Asynchronous callback when socket ready
|
||||||
void operator()(const wait_type &, const milliseconds &timeout, handler);
|
void operator()(const wait_type &, const milliseconds &timeout, ec_handler);
|
||||||
void operator()(const wait_type &, handler);
|
void operator()(const wait_type &, ec_handler);
|
||||||
bool cancel() noexcept;
|
bool cancel() noexcept;
|
||||||
|
|
||||||
void handshake(const connopts &, handler callback);
|
void disconnect(const close_opts &, eptr_handler);
|
||||||
void connect(const endpoint &, const connopts &, handler callback);
|
void handshake(const open_opts &, eptr_handler);
|
||||||
|
void connect(const endpoint &, const open_opts &, eptr_handler);
|
||||||
bool disconnect(const dc &type);
|
|
||||||
|
|
||||||
socket(asio::ssl::context &ssl = sslv23_client,
|
socket(asio::ssl::context &ssl = sslv23_client,
|
||||||
boost::asio::io_service *const &ios = ircd::ios);
|
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 cancel() noexcept; // invoke timer.cancel() before dtor
|
||||||
bool release(); // cancels the cancel;
|
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(socket &, const milliseconds &timeout);
|
||||||
scope_timeout() = default;
|
scope_timeout() = default;
|
||||||
scope_timeout(scope_timeout &&) noexcept;
|
scope_timeout(scope_timeout &&) noexcept;
|
||||||
|
@ -134,135 +132,106 @@ class ircd::net::socket::scope_timeout
|
||||||
~scope_timeout() noexcept;
|
~scope_timeout() noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ircd::net::socket::io
|
/// Yields ircd::ctx until buffers are full.
|
||||||
{
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class iov>
|
template<class iov>
|
||||||
auto
|
size_t
|
||||||
ircd::net::socket::write(const iov &bufs)
|
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>
|
const size_t ret
|
||||||
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
|
|
||||||
{
|
{
|
||||||
io{*this, out, bytes};
|
asio::async_read(ssl, std::forward<iov>(bufs), completion, yield_context{to_asio{}})
|
||||||
handler(xfer{ec, bytes});
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class iov>
|
if(!ret)
|
||||||
auto
|
throw boost::system::system_error
|
||||||
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
|
|
||||||
{
|
{
|
||||||
async_read(ssl, bufs, yield_context{to_asio{}})
|
boost::asio::error::eof
|
||||||
};
|
};
|
||||||
|
|
||||||
if(unlikely(!ret))
|
in.bytes += ret;
|
||||||
throw boost::system::system_error(boost::asio::error::eof);
|
++in.calls;
|
||||||
|
return ret;
|
||||||
return ret;
|
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Yields ircd::ctx until remote has sent at least some data.
|
||||||
template<class iov>
|
template<class iov>
|
||||||
auto
|
size_t
|
||||||
ircd::net::socket::read(const iov &bufs,
|
ircd::net::socket::read_any(iov&& bufs)
|
||||||
xfer_handler handler)
|
|
||||||
{
|
{
|
||||||
async_read(ssl, bufs, [this, handler(std::move(handler))]
|
const size_t ret
|
||||||
(const error_code &ec, const size_t &bytes)
|
|
||||||
noexcept
|
|
||||||
{
|
{
|
||||||
io{*this, in, bytes};
|
ssl.async_read_some(std::forward<iov>(bufs), yield_context{to_asio{}})
|
||||||
handler(xfer{ec, bytes});
|
};
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class iov>
|
if(!ret)
|
||||||
auto
|
throw boost::system::system_error
|
||||||
ircd::net::socket::read_some(const iov &bufs)
|
|
||||||
{
|
|
||||||
return io{*this, in, [&]
|
|
||||||
{
|
|
||||||
const size_t ret
|
|
||||||
{
|
{
|
||||||
ssl.async_read_some(bufs, yield_context{to_asio{}})
|
boost::asio::error::eof
|
||||||
};
|
};
|
||||||
|
|
||||||
if(unlikely(!ret))
|
in.bytes += ret;
|
||||||
throw boost::system::system_error(boost::asio::error::eof);
|
++in.calls;
|
||||||
|
return ret;
|
||||||
return ret;
|
|
||||||
}};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Yields ircd::ctx until all buffers are sent.
|
||||||
template<class iov>
|
template<class iov>
|
||||||
auto
|
size_t
|
||||||
ircd::net::socket::read_some(const iov &bufs,
|
ircd::net::socket::write_all(iov&& bufs)
|
||||||
xfer_handler handler)
|
|
||||||
{
|
{
|
||||||
ssl.async_read_some(bufs, [this, handler(std::move(handler))]
|
static const auto completion
|
||||||
(const error_code &ec, const size_t &bytes)
|
|
||||||
noexcept
|
|
||||||
{
|
{
|
||||||
io{*this, in, bytes};
|
asio::transfer_all()
|
||||||
handler(xfer{ec, bytes});
|
};
|
||||||
});
|
|
||||||
|
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);
|
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::const_raw_buffer
|
||||||
ircd::net::peer_cert_der(const mutable_raw_buffer &buf,
|
ircd::net::peer_cert_der(const mutable_raw_buffer &buf,
|
||||||
const socket &socket)
|
const socket &socket)
|
||||||
|
@ -269,8 +122,239 @@ catch(...)
|
||||||
return false;
|
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
|
/// Construct sockopts with the current options from socket argument
|
||||||
|
@ -407,6 +491,30 @@ ircd::net::nodelay(socket &socket,
|
||||||
sd.set_option(option);
|
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
|
void
|
||||||
ircd::net::blocking(socket &socket,
|
ircd::net::blocking(socket &socket,
|
||||||
const bool &b)
|
const bool &b)
|
||||||
|
@ -511,11 +619,11 @@ struct ircd::net::listener::acceptor
|
||||||
void configure(const json::object &opts);
|
void configure(const json::object &opts);
|
||||||
|
|
||||||
// Handshake stack
|
// 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;
|
void handshake(const error_code &ec, std::shared_ptr<socket>, std::weak_ptr<acceptor>) noexcept;
|
||||||
|
|
||||||
// Acceptance stack
|
// 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;
|
void accept(const error_code &ec, std::shared_ptr<socket>, std::weak_ptr<acceptor>) noexcept;
|
||||||
|
|
||||||
// Accept next
|
// Accept next
|
||||||
|
@ -716,21 +824,19 @@ noexcept try
|
||||||
const unwind::exceptional drop{[&sock]
|
const unwind::exceptional drop{[&sock]
|
||||||
{
|
{
|
||||||
assert(bool(sock));
|
assert(bool(sock));
|
||||||
disconnect(*sock, dc::RST);
|
close(*sock, dc::RST, close_ignore);
|
||||||
}};
|
}};
|
||||||
|
|
||||||
assert(bool(sock));
|
assert(bool(sock));
|
||||||
if(unlikely(accept_error(ec, *sock)))
|
check_accept_error(ec, *sock);
|
||||||
{
|
|
||||||
disconnect(*sock, dc::RST);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("%s: socket(%p) accepted %s",
|
log.debug("%s: socket(%p) accepted %s",
|
||||||
std::string(*this),
|
std::string(*this),
|
||||||
sock.get(),
|
sock.get(),
|
||||||
string(sock->remote()));
|
string(sock->remote()));
|
||||||
|
|
||||||
|
// Toggles the behavior of non-async functions; see func comment
|
||||||
|
blocking(*sock, false);
|
||||||
|
|
||||||
static const socket::handshake_type handshake_type
|
static const socket::handshake_type handshake_type
|
||||||
{
|
{
|
||||||
socket::handshake_type::server
|
socket::handshake_type::server
|
||||||
|
@ -766,9 +872,9 @@ catch(const std::exception &e)
|
||||||
/// whether or not the handler should return or continue processing the
|
/// whether or not the handler should return or continue processing the
|
||||||
/// result.
|
/// result.
|
||||||
///
|
///
|
||||||
bool
|
void
|
||||||
ircd::net::listener::acceptor::accept_error(const error_code &ec,
|
ircd::net::listener::acceptor::check_accept_error(const error_code &ec,
|
||||||
socket &sock)
|
socket &sock)
|
||||||
{
|
{
|
||||||
using namespace boost::system::errc;
|
using namespace boost::system::errc;
|
||||||
using boost::system::system_category;
|
using boost::system::system_category;
|
||||||
|
@ -777,12 +883,12 @@ ircd::net::listener::acceptor::accept_error(const error_code &ec,
|
||||||
throw ctx::interrupted();
|
throw ctx::interrupted();
|
||||||
|
|
||||||
if(likely(ec == success))
|
if(likely(ec == success))
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
if(ec.category() == system_category()) switch(ec.value())
|
if(ec.category() == system_category()) switch(ec.value())
|
||||||
{
|
{
|
||||||
case operation_canceled:
|
case operation_canceled:
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -801,18 +907,14 @@ noexcept try
|
||||||
return;
|
return;
|
||||||
|
|
||||||
--handshaking;
|
--handshaking;
|
||||||
assert(bool(sock));
|
|
||||||
const unwind::exceptional drop{[&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)))
|
assert(bool(sock));
|
||||||
{
|
check_handshake_error(ec, *sock);
|
||||||
disconnect(*sock, dc::RST);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.debug("%s socket(%p): SSL handshook %s",
|
log.debug("%s socket(%p): SSL handshook %s",
|
||||||
std::string(*this),
|
std::string(*this),
|
||||||
sock.get(),
|
sock.get(),
|
||||||
|
@ -842,9 +944,9 @@ catch(const std::exception &e)
|
||||||
/// whether or not the handler should return or continue processing the
|
/// whether or not the handler should return or continue processing the
|
||||||
/// result.
|
/// result.
|
||||||
///
|
///
|
||||||
bool
|
void
|
||||||
ircd::net::listener::acceptor::handshake_error(const error_code &ec,
|
ircd::net::listener::acceptor::check_handshake_error(const error_code &ec,
|
||||||
socket &sock)
|
socket &sock)
|
||||||
{
|
{
|
||||||
using boost::system::system_category;
|
using boost::system::system_category;
|
||||||
using namespace boost::system::errc;
|
using namespace boost::system::errc;
|
||||||
|
@ -853,12 +955,12 @@ ircd::net::listener::acceptor::handshake_error(const error_code &ec,
|
||||||
throw ctx::interrupted();
|
throw ctx::interrupted();
|
||||||
|
|
||||||
if(likely(ec == success))
|
if(likely(ec == success))
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
if(ec.category() == system_category()) switch(ec.value())
|
if(ec.category() == system_category()) switch(ec.value())
|
||||||
{
|
{
|
||||||
case operation_canceled:
|
case operation_canceled:
|
||||||
return false;
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -1032,8 +1134,8 @@ catch(const std::exception &e)
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::net::socket::connect(const endpoint &ep,
|
ircd::net::socket::connect(const endpoint &ep,
|
||||||
const connopts &opts,
|
const open_opts &opts,
|
||||||
handler callback)
|
eptr_handler callback)
|
||||||
{
|
{
|
||||||
log.debug("socket(%p) attempting connect to remote: %s for the next %ld$ms",
|
log.debug("socket(%p) attempting connect to remote: %s for the next %ld$ms",
|
||||||
this,
|
this,
|
||||||
|
@ -1050,8 +1152,8 @@ ircd::net::socket::connect(const endpoint &ep,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::net::socket::handshake(const connopts &opts,
|
ircd::net::socket::handshake(const open_opts &opts,
|
||||||
handler callback)
|
eptr_handler callback)
|
||||||
{
|
{
|
||||||
log.debug("socket(%p) performing handshake with %s for '%s' for the next %ld$ms",
|
log.debug("socket(%p) performing handshake with %s for '%s' for the next %ld$ms",
|
||||||
this,
|
this,
|
||||||
|
@ -1074,101 +1176,72 @@ ircd::net::socket::handshake(const connopts &opts,
|
||||||
set_timeout(opts.handshake_timeout);
|
set_timeout(opts.handshake_timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
ircd::net::socket::disconnect(const dc &type)
|
ircd::net::socket::disconnect(const close_opts &opts,
|
||||||
|
eptr_handler callback)
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(timer.expires_from_now() > 0ms)
|
if(!sd.is_open())
|
||||||
timer.cancel();
|
{
|
||||||
|
call_user(callback, {});
|
||||||
if(sd.is_open())
|
return;
|
||||||
log.debug("socket(%p): disconnect: %s type:%d user: in:%zu out:%zu",
|
}
|
||||||
(const void *)this,
|
|
||||||
ircd::string(remote_ipport(*this)),
|
const bool cancelation{cancel()};
|
||||||
uint(type),
|
log.debug("socket(%p): disconnect: %s type:%d user: in:%zu out:%zu cancel:%d",
|
||||||
in.bytes,
|
(const void *)this,
|
||||||
out.bytes);
|
ircd::string(remote_ipport(*this)),
|
||||||
|
uint(opts.type),
|
||||||
if(sd.is_open()) switch(type)
|
in.bytes,
|
||||||
|
out.bytes,
|
||||||
|
cancelation);
|
||||||
|
|
||||||
|
if(opts.sopts)
|
||||||
|
set(*this, *opts.sopts);
|
||||||
|
|
||||||
|
switch(opts.type)
|
||||||
{
|
{
|
||||||
default:
|
|
||||||
case dc::RST:
|
case dc::RST:
|
||||||
sd.close();
|
sd.close();
|
||||||
return true;
|
break;
|
||||||
|
|
||||||
case dc::FIN:
|
case dc::FIN:
|
||||||
sd.shutdown(ip::tcp::socket::shutdown_both);
|
sd.shutdown(ip::tcp::socket::shutdown_both);
|
||||||
return true;
|
break;
|
||||||
|
|
||||||
case dc::FIN_SEND:
|
case dc::FIN_SEND:
|
||||||
sd.shutdown(ip::tcp::socket::shutdown_send);
|
sd.shutdown(ip::tcp::socket::shutdown_send);
|
||||||
return true;
|
break;
|
||||||
|
|
||||||
case dc::FIN_RECV:
|
case dc::FIN_RECV:
|
||||||
sd.shutdown(ip::tcp::socket::shutdown_receive);
|
sd.shutdown(ip::tcp::socket::shutdown_receive);
|
||||||
return true;
|
break;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
case dc::SSL_NOTIFY:
|
case dc::SSL_NOTIFY:
|
||||||
{
|
{
|
||||||
ssl.async_shutdown([s(shared_from_this())]
|
auto disconnect_handler
|
||||||
(error_code ec)
|
|
||||||
noexcept
|
|
||||||
{
|
{
|
||||||
if(!s->timedout)
|
std::bind(&socket::handle_disconnect, this, shared_from(*this), std::move(callback), ph::_1)
|
||||||
s->cancel_timeout();
|
};
|
||||||
|
|
||||||
if(ec)
|
ssl.async_shutdown(std::move(disconnect_handler));
|
||||||
log.warning("socket(%p): SSL_NOTIFY: %s: %s",
|
set_timeout(opts.timeout);
|
||||||
s.get(),
|
return;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else return false;
|
|
||||||
|
call_user(callback, {});
|
||||||
}
|
}
|
||||||
catch(const boost::system::system_error &e)
|
catch(const boost::system::system_error &e)
|
||||||
{
|
{
|
||||||
log.warning("socket(%p): disconnect: type: %d: %s",
|
call_user(callback, e.code());
|
||||||
(const void *)this,
|
}
|
||||||
uint(type),
|
catch(const std::exception &e)
|
||||||
e.what());
|
{
|
||||||
|
log.critical("socket(%p): disconnect: type: %d: %s",
|
||||||
if(sd.is_open())
|
(const void *)this,
|
||||||
{
|
uint(opts.type),
|
||||||
boost::system::error_code ec;
|
e.what());
|
||||||
sd.close(ec);
|
|
||||||
if(ec)
|
|
||||||
log.warning("socket(%p): after disconnect: %s: %s",
|
|
||||||
this,
|
|
||||||
string(ec));
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1193,7 +1266,7 @@ noexcept
|
||||||
///
|
///
|
||||||
void
|
void
|
||||||
ircd::net::socket::operator()(const wait_type &type,
|
ircd::net::socket::operator()(const wait_type &type,
|
||||||
handler h)
|
ec_handler h)
|
||||||
{
|
{
|
||||||
operator()(type, milliseconds(-1), std::move(h));
|
operator()(type, milliseconds(-1), std::move(h));
|
||||||
}
|
}
|
||||||
|
@ -1206,7 +1279,7 @@ ircd::net::socket::operator()(const wait_type &type,
|
||||||
void
|
void
|
||||||
ircd::net::socket::operator()(const wait_type &type,
|
ircd::net::socket::operator()(const wait_type &type,
|
||||||
const milliseconds &timeout,
|
const milliseconds &timeout,
|
||||||
handler callback)
|
ec_handler callback)
|
||||||
{
|
{
|
||||||
auto handle
|
auto handle
|
||||||
{
|
{
|
||||||
|
@ -1242,7 +1315,7 @@ ircd::net::socket::operator()(const wait_type &type,
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::net::socket::handle(const std::weak_ptr<socket> wp,
|
ircd::net::socket::handle(const std::weak_ptr<socket> wp,
|
||||||
const handler callback,
|
const ec_handler callback,
|
||||||
const error_code &ec)
|
const error_code &ec)
|
||||||
noexcept try
|
noexcept try
|
||||||
{
|
{
|
||||||
|
@ -1279,6 +1352,15 @@ noexcept try
|
||||||
|
|
||||||
call_user(callback, ec);
|
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)
|
catch(const std::bad_weak_ptr &e)
|
||||||
{
|
{
|
||||||
// This handler may still be registered with asio after the socket destructs, so
|
// 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)",
|
log.warning("socket(%p): belated callback to handler... (%s)",
|
||||||
this,
|
this,
|
||||||
e.what());
|
e.what());
|
||||||
|
|
||||||
assert(0);
|
assert(0);
|
||||||
|
call_user(callback, ec);
|
||||||
}
|
}
|
||||||
catch(const std::exception &e)
|
catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
log.critical("socket(%p): handle: %s",
|
log.critical("socket(%p): handle: %s",
|
||||||
this,
|
this,
|
||||||
e.what());
|
e.what());
|
||||||
|
|
||||||
assert(0);
|
assert(0);
|
||||||
|
call_user(callback, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1345,8 +1431,8 @@ catch(const std::exception &e)
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::net::socket::handle_connect(std::weak_ptr<socket> wp,
|
ircd::net::socket::handle_connect(std::weak_ptr<socket> wp,
|
||||||
const connopts &opts,
|
const open_opts &opts,
|
||||||
handler callback,
|
eptr_handler callback,
|
||||||
const error_code &ec)
|
const error_code &ec)
|
||||||
noexcept try
|
noexcept try
|
||||||
{
|
{
|
||||||
|
@ -1366,16 +1452,13 @@ noexcept try
|
||||||
if(ec)
|
if(ec)
|
||||||
return call_user(callback, ec);
|
return call_user(callback, ec);
|
||||||
|
|
||||||
// Try to set the user's socket options now; if something fails
|
// Toggles the behavior of non-async functions; see func comment
|
||||||
// we can invoke their callback with the error.
|
blocking(*this, false);
|
||||||
if(opts.sopts) try
|
|
||||||
{
|
// 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);
|
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.
|
// The user can opt out of performing the handshake here.
|
||||||
if(!opts.handshake)
|
if(!opts.handshake)
|
||||||
|
@ -1388,20 +1471,74 @@ catch(const std::bad_weak_ptr &e)
|
||||||
log.warning("socket(%p): belated callback to handle_connect... (%s)",
|
log.warning("socket(%p): belated callback to handle_connect... (%s)",
|
||||||
this,
|
this,
|
||||||
e.what());
|
e.what());
|
||||||
|
|
||||||
assert(0);
|
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)
|
catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
log.critical("socket(%p): handle_connect: %s",
|
log.critical("socket(%p): handle_connect: %s",
|
||||||
this,
|
this,
|
||||||
e.what());
|
e.what());
|
||||||
|
|
||||||
assert(0);
|
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
|
void
|
||||||
ircd::net::socket::handle_handshake(std::weak_ptr<socket> wp,
|
ircd::net::socket::handle_handshake(std::weak_ptr<socket> wp,
|
||||||
const connopts &opts,
|
const open_opts &opts,
|
||||||
handler callback,
|
eptr_handler callback,
|
||||||
const error_code &ec)
|
const error_code &ec)
|
||||||
noexcept try
|
noexcept try
|
||||||
{
|
{
|
||||||
|
@ -1421,12 +1558,22 @@ noexcept try
|
||||||
// back with or without error here.
|
// back with or without error here.
|
||||||
call_user(callback, ec);
|
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)
|
catch(const std::bad_weak_ptr &e)
|
||||||
{
|
{
|
||||||
log.warning("socket(%p): belated callback to handle_handshake... (%s)",
|
log.warning("socket(%p): belated callback to handle_handshake... (%s)",
|
||||||
this,
|
this,
|
||||||
e.what());
|
e.what());
|
||||||
assert(0);
|
assert(0);
|
||||||
|
call_user(callback, ec);
|
||||||
}
|
}
|
||||||
catch(const std::exception &e)
|
catch(const std::exception &e)
|
||||||
{
|
{
|
||||||
|
@ -1434,12 +1581,13 @@ catch(const std::exception &e)
|
||||||
this,
|
this,
|
||||||
e.what());
|
e.what());
|
||||||
assert(0);
|
assert(0);
|
||||||
|
call_user(callback, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ircd::net::socket::handle_verify(const bool valid,
|
ircd::net::socket::handle_verify(const bool valid,
|
||||||
asio::ssl::verify_context &vc,
|
asio::ssl::verify_context &vc,
|
||||||
const connopts &opts)
|
const open_opts &opts)
|
||||||
noexcept try
|
noexcept try
|
||||||
{
|
{
|
||||||
// `valid` indicates whether or not there's an anomaly with the
|
// `valid` indicates whether or not there's an anomaly with the
|
||||||
|
@ -1546,7 +1694,7 @@ catch(const std::exception &e)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::net::socket::call_user(const handler &callback,
|
ircd::net::socket::call_user(const ec_handler &callback,
|
||||||
const error_code &ec)
|
const error_code &ec)
|
||||||
noexcept try
|
noexcept try
|
||||||
{
|
{
|
||||||
|
@ -1559,6 +1707,20 @@ catch(const std::exception &e)
|
||||||
e.what());
|
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
|
boost::asio::ip::tcp::endpoint
|
||||||
ircd::net::socket::local()
|
ircd::net::socket::local()
|
||||||
const
|
const
|
||||||
|
@ -1602,7 +1764,7 @@ ircd::net::socket::set_timeout(const milliseconds &t)
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::net::socket::set_timeout(const milliseconds &t,
|
ircd::net::socket::set_timeout(const milliseconds &t,
|
||||||
handler h)
|
ec_handler h)
|
||||||
{
|
{
|
||||||
cancel_timeout();
|
cancel_timeout();
|
||||||
if(t < milliseconds(0))
|
if(t < milliseconds(0))
|
||||||
|
@ -1636,36 +1798,6 @@ const
|
||||||
return *ssl.native_handle();
|
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
|
// 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,
|
ircd::net::socket::scope_timeout::scope_timeout(socket &socket,
|
||||||
const milliseconds &timeout,
|
const milliseconds &timeout,
|
||||||
socket::handler handler)
|
socket::ec_handler handler)
|
||||||
:s{&socket}
|
:s{&socket}
|
||||||
{
|
{
|
||||||
socket.set_timeout(timeout, std::move(handler));
|
socket.set_timeout(timeout, std::move(handler));
|
||||||
|
@ -1736,17 +1868,6 @@ ircd::net::socket::scope_timeout::release()
|
||||||
return s != nullptr;
|
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
|
// net/resolve.h
|
||||||
|
|
Loading…
Reference in a new issue