Mercurial > irccd
changeset 237:93b21652404f
Irccd: unify transports in transport.[hc]pp
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 12 Aug 2016 15:34:03 +0200 |
parents | 5fa15cd7ffe0 |
children | 772656d4ba8d |
files | lib/irccd/CMakeSources.cmake lib/irccd/config.cpp lib/irccd/service-transport.cpp lib/irccd/transport-client.cpp lib/irccd/transport-client.hpp lib/irccd/transport-server.cpp lib/irccd/transport-server.hpp lib/irccd/transport.cpp lib/irccd/transport.hpp |
diffstat | 9 files changed, 667 insertions(+), 673 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/irccd/CMakeSources.cmake Fri Aug 12 15:16:39 2016 +0200 +++ b/lib/irccd/CMakeSources.cmake Fri Aug 12 15:34:03 2016 +0200 @@ -74,8 +74,7 @@ ${CMAKE_CURRENT_LIST_DIR}/service-transport.hpp ${CMAKE_CURRENT_LIST_DIR}/system.hpp ${CMAKE_CURRENT_LIST_DIR}/timer.hpp - ${CMAKE_CURRENT_LIST_DIR}/transport-client.hpp - ${CMAKE_CURRENT_LIST_DIR}/transport-server.hpp + ${CMAKE_CURRENT_LIST_DIR}/transport.hpp ${CMAKE_CURRENT_LIST_DIR}/unicode.hpp ${CMAKE_CURRENT_LIST_DIR}/util.hpp ) @@ -150,8 +149,7 @@ ${CMAKE_CURRENT_LIST_DIR}/service-transport.cpp ${CMAKE_CURRENT_LIST_DIR}/system.cpp ${CMAKE_CURRENT_LIST_DIR}/timer.cpp - ${CMAKE_CURRENT_LIST_DIR}/transport-client.cpp - ${CMAKE_CURRENT_LIST_DIR}/transport-server.cpp + ${CMAKE_CURRENT_LIST_DIR}/transport.cpp ${CMAKE_CURRENT_LIST_DIR}/unicode.cpp ${CMAKE_CURRENT_LIST_DIR}/util.cpp )
--- a/lib/irccd/config.cpp Fri Aug 12 15:16:39 2016 +0200 +++ b/lib/irccd/config.cpp Fri Aug 12 15:34:03 2016 +0200 @@ -30,7 +30,7 @@ #include "server.hpp" #include "service-plugin.hpp" #include "sysconfig.hpp" -#include "transport-server.hpp" +#include "transport.hpp" #include "util.hpp" using namespace fmt::literals;
--- a/lib/irccd/service-transport.cpp Fri Aug 12 15:16:39 2016 +0200 +++ b/lib/irccd/service-transport.cpp Fri Aug 12 15:34:03 2016 +0200 @@ -22,8 +22,7 @@ #include "logger.hpp" #include "service-command.hpp" #include "service-transport.hpp" -#include "transport-client.hpp" -#include "transport-server.hpp" +#include "transport.hpp" namespace irccd {
--- a/lib/irccd/transport-client.cpp Fri Aug 12 15:16:39 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ -/* - * transport-client.cpp -- client connected to irccd - * - * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "json.hpp" -#include "logger.hpp" -#include "transport-client.hpp" - -namespace irccd { - -void TransportClient::parse(const std::string &message) -{ - auto document = nlohmann::json::parse(message); - - if (document.is_object()) - onCommand(document); -} - -unsigned TransportClient::recv(char *buffer, unsigned length) -{ - return m_socket.recv(buffer, length); -} - -unsigned TransportClient::send(const char *buffer, unsigned length) -{ - return m_socket.send(buffer, length); -} - -void TransportClient::syncInput() -{ - try { - std::string buffer; - - buffer.resize(512); - buffer.resize(recv(&buffer[0], buffer.size())); - - if (buffer.empty()) - onDie(); - - m_input += std::move(buffer); - } catch (const std::exception &) { - onDie(); - } -} - -void TransportClient::syncOutput() -{ - try { - auto ns = send(&m_output[0], m_output.size()); - - if (ns == 0) - onDie(); - - m_output.erase(0, ns); - } catch (const std::exception &ex) { - onDie(); - } -} - -void TransportClient::prepare(fd_set &in, fd_set &out, net::Handle &max) -{ - if (m_socket.handle() > max) - max = m_socket.handle(); - - FD_SET(m_socket.handle(), &in); - - if (!m_output.empty()) - FD_SET(m_socket.handle(), &out); -} - -void TransportClient::sync(fd_set &in, fd_set &out) -{ - // Do some I/O. - if (FD_ISSET(m_socket.handle(), &in)) { - log::debug() << "transport: receiving to input buffer" << std::endl; - syncInput(); - } - if (FD_ISSET(m_socket.handle(), &out)) { - log::debug() << "transport: sending outgoing buffer" << std::endl; - syncOutput(); - } - - // Flush the queue. - for (std::size_t pos; (pos = m_input.find("\r\n\r\n")) != std::string::npos; ) { - auto message = m_input.substr(0, pos); - - m_input.erase(m_input.begin(), m_input.begin() + pos + 4); - - parse(message); - } -} - -void TransportClient::send(const nlohmann::json &json) -{ - assert(json.is_object()); - - m_output += json.dump(); - m_output += "\r\n\r\n"; -} - -/* - * TransportClientTls - * ------------------------------------------------------------------ - */ - -void TransportClientTls::handshake() -{ - try { - m_ssl.handshake(); - m_handshake = HandshakeReady; - } catch (const net::WantReadError &) { - m_handshake = HandshakeRead; - } catch (const net::WantWriteError &) { - m_handshake = HandshakeWrite; - } catch (const std::exception &) { - onDie(); - } -} - -TransportClientTls::TransportClientTls(const std::string &pkey, - const std::string &cert, - net::TcpSocket socket) - : TransportClient(std::move(socket)) - , m_ssl(m_socket) -{ - m_ssl.setPrivateKey(pkey); - m_ssl.setCertificate(cert); - - handshake(); -} - -unsigned TransportClientTls::recv(char *buffer, unsigned length) -{ - unsigned nread = 0; - - try { - nread = m_ssl.recv(buffer, length); - } catch (const net::WantReadError &) { - m_handshake = HandshakeRead; - } catch (const net::WantWriteError &) { - m_handshake = HandshakeWrite; - } - - return nread; -} - -unsigned TransportClientTls::send(const char *buffer, unsigned length) -{ - unsigned nsent = 0; - - try { - nsent = m_ssl.send(buffer, length); - } catch (const net::WantReadError &) { - m_handshake = HandshakeRead; - } catch (const net::WantWriteError &) { - m_handshake = HandshakeWrite; - } - - return nsent; -} - -void TransportClientTls::prepare(fd_set &in, fd_set &out, net::Handle &max) -{ - if (m_socket.handle() > max) - max = m_socket.handle(); - - switch (m_handshake) { - case HandshakeRead: - FD_SET(m_socket.handle(), &in); - break; - case HandshakeWrite: - FD_SET(m_socket.handle(), &out); - break; - default: - TransportClient::prepare(in, out, max); - break; - } -} - -void TransportClientTls::sync(fd_set &in, fd_set &out) -{ - switch (m_handshake) { - case HandshakeRead: - case HandshakeWrite: - handshake(); - break; - default: - TransportClient::sync(in, out); - } -} - -} // !irccd
--- a/lib/irccd/transport-client.hpp Fri Aug 12 15:16:39 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,165 +0,0 @@ -/* - * transport-client.hpp -- client connected to irccd - * - * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_TRANSPORT_CLIENT_HPP -#define IRCCD_TRANSPORT_CLIENT_HPP - -/** - * \file transport-client.hpp - * \brief Client connected to irccd - */ - -#include <functional> -#include <memory> -#include <stdexcept> -#include <string> - -#include <json.hpp> - -#include "net.hpp" -#include "server.hpp" -#include "signals.hpp" -#include "sysconfig.hpp" - -namespace irccd { - -/** - * \class TransportClient - * \brief Client connected to irccd. - * - * This class emits a warning upon clients request through onCommand signal. - */ -class TransportClient { -public: - /** - * Signal: onCommand - * ---------------------------------------------------------- - * - * Arguments: - * - the command - */ - Signal<const nlohmann::json &> onCommand; - - /** - * Signal: onDie - * ---------------------------------------------------------- - * - * The client has disconnected. - */ - Signal<> onDie; - -protected: - net::TcpSocket m_socket; //!< socket - std::string m_input; //!< input buffer - std::string m_output; //!< output buffer - - /** - * Parse input buffer. - * - * \param buffer the buffer. - */ - void parse(const std::string &buffer); - -protected: - /** - * Try to receive some data into the given buffer. - * - * \param buffer the destination buffer - * \param length the buffer length - * \return the number of bytes received - */ - virtual unsigned recv(char *buffer, unsigned length); - - /** - * Try to send some data into the given buffer. - * - * \param buffer the source buffer - * \param length the buffer length - * \return the number of bytes sent - */ - virtual unsigned send(const char *buffer, unsigned length); - -public: - inline TransportClient(net::TcpSocket socket) - : m_socket(std::move(socket)) - { - m_socket.set(net::option::SockBlockMode(false)); - } - - /** - * Convenient wrapper around recv(). - * - * Must be used in sync() function. - */ - void syncInput(); - - /** - * Convenient wrapper around send(). - * - * Must be used in sync() function. - */ - void syncOutput(); - - /** - * Virtual destructor defaulted. - */ - virtual ~TransportClient() = default; - - IRCCD_EXPORT void send(const nlohmann::json &json); - - IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max); - - IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out); -}; - -class TransportClientTls : public TransportClient { -private: - enum { - HandshakeWrite, - HandshakeRead, - HandshakeReady - } m_handshake{HandshakeReady}; - - net::TlsSocket m_ssl; - - void handshake(); - -protected: - /** - * \copydoc TransportClient::recv - */ - unsigned recv(char *buffer, unsigned length) override; - - /** - * \copydoc TransportClient::send - */ - unsigned send(const char *buffer, unsigned length) override; - -public: - IRCCD_EXPORT TransportClientTls(const std::string &pkey, - const std::string &cert, - net::TcpSocket socket); - - IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max); - - IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out); -}; - -} // !irccd - -#endif // !IRCCD_TRANSPORT_CLIENT_HPP
--- a/lib/irccd/transport-server.cpp Fri Aug 12 15:16:39 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,114 +0,0 @@ -/* - * transport-server.cpp -- I/O for irccd clients (acceptors) - * - * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "sysconfig.hpp" - -#if !defined(IRCCD_SYSTEM_WINDOWS) -# include <cstdio> -#endif - -#include <sstream> - -#include "logger.hpp" -#include "transport-server.hpp" - -namespace irccd { - -/* - * TransportServerIp - * ------------------------------------------------------------------ - */ - -TransportServerIp::TransportServerIp(const std::string &address, - std::uint16_t port, - std::uint8_t mode) - : TransportServer(net::TcpSocket((mode & v6) ? AF_INET6 : AF_INET, 0)) -{ - assert((mode & v6) || (mode & v4)); - - m_socket.set(net::option::SockReuseAddress(true)); - - if (mode & v6) { - if (address == "*") - m_socket.bind(net::ipv6::any(port)); - else - m_socket.bind(net::ipv6::pton(address, port)); - - // Disable or enable IPv4 when using IPv6. - if (!(mode & v4)) - m_socket.set(net::option::Ipv6Only(true)); - } else { - if (address == "*") - m_socket.bind(net::ipv4::any(port)); - else - m_socket.bind(net::ipv4::pton(address, port)); - } - - m_socket.listen(); - - log::info() << "transport: listening on " << address << ", port " << port << std::endl; -} - -/* - * TransportServerTls - * ------------------------------------------------------------------ - */ - -TransportServerTls::TransportServerTls(const std::string &pkey, - const std::string &cert, - const std::string &address, - std::uint16_t port, - std::uint8_t mode) - : TransportServerIp(address, port, mode) - , m_privatekey(pkey) - , m_cert(cert) -{ - log::info() << "transport: listening on " << address << ", port " << port - << " (using SSL)" << std::endl; -} - -std::unique_ptr<TransportClient> TransportServerTls::accept() -{ - return std::make_unique<TransportClientTls>(m_privatekey, m_cert, m_socket.accept()); -} - -/* - * TransportServerUnix - * ------------------------------------------------------------------ - */ - -#if !defined(IRCCD_SYSTEM_WINDOWS) - -TransportServerLocal::TransportServerLocal(std::string path) - : TransportServer(net::TcpSocket(AF_LOCAL, 0)) - , m_path(std::move(path)) -{ - m_socket.bind(net::local::create(m_path, true)); - m_socket.listen(); - - log::info() << "transport: listening on " << m_path << std::endl; -} - -TransportServerLocal::~TransportServerLocal() -{ - ::remove(m_path.c_str()); -} - -#endif - -} // !irccd
--- a/lib/irccd/transport-server.hpp Fri Aug 12 15:16:39 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -/* - * transport-server.hpp -- I/O for irccd clients (acceptors) - * - * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> - * - * 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 appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#ifndef IRCCD_TRANSPORT_SERVER_HPP -#define IRCCD_TRANSPORT_SERVER_HPP - -/** - * \file transport-server.hpp - * \brief Transports for irccd - */ - -#include <memory> -#include <string> - -#include "net.hpp" -#include "sysconfig.hpp" -#include "transport-client.hpp" - -namespace irccd { - -/** - * \brief Bring networking between irccd and irccdctl. - * - * This class contains a master sockets for listening to TCP connections, it is then processed by irccd. - * - * The transport class supports the following domains: - * - * | Domain | Class | - * |-----------------------|-----------------------| - * | IPv4, IPv6 | TransportServerIp | - * | Unix (not on Windows) | TransportServerUnix | - * - * Note: IPv4 and IPv6 can be combined, using TransportServer::IPv6 and its option. - */ -class TransportServer { -private: - TransportServer(const TransportServer &) = delete; - TransportServer(TransportServer &&) = delete; - - TransportServer &operator=(const TransportServer &) = delete; - TransportServer &operator=(TransportServer &&) = delete; - -protected: - /** - * The socket handle. - */ - net::TcpSocket m_socket; - -public: - /** - * Default constructor. - */ - inline TransportServer(net::TcpSocket socket) - : m_socket(std::move(socket)) - { - } - - /** - * Get the socket handle for this transport. - * - * \return the handle - */ - inline net::Handle handle() const noexcept - { - return m_socket.handle(); - } - - /** - * Destructor defaulted. - */ - virtual ~TransportServer() = default; - - /** - * Accept a new client depending on the domain. - * - * \return the new client - */ - virtual std::unique_ptr<TransportClient> accept() - { - return std::make_unique<TransportClient>(m_socket.accept()); - } -}; - -/** - * \brief Create IP transport. - */ -class TransportServerIp : public TransportServer { -public: - /** - * \brief Domain to use. - */ - enum Mode { - v4 = (1 << 0), //!< IPv6 - v6 = (1 << 1) //!< IPv4 - }; - - /** - * Constructor. - * \pre mode > 0 - * \param address the address (* for any) - * \param port the port number - * \param mode the domains to use (can be OR'ed) - */ - IRCCD_EXPORT TransportServerIp(const std::string &address, - std::uint16_t port, - std::uint8_t mode = v4); -}; - -/** - * \brief TLS over IP transport. - */ -class TransportServerTls : public TransportServerIp { -private: - std::string m_privatekey; - std::string m_cert; - -public: - /** - * Constructor. - * \pre mode > 0 - * \param pkey the private key file - * \param cert the certificate file - * \param address the address (* for any) - * \param port the port number - * \param mode the domains to use (can be OR'ed) - */ - IRCCD_EXPORT TransportServerTls(const std::string &pkey, - const std::string &cert, - const std::string &address, - std::uint16_t port, - std::uint8_t mode = v4); - - /** - * \copydoc TransportServer::accept - */ - IRCCD_EXPORT std::unique_ptr<TransportClient> accept() override; -}; - -#if !defined(IRCCD_SYSTEM_WINDOWS) - -/** - * \brief Implementation of transports for Unix sockets. - */ -class TransportServerLocal : public TransportServer { -private: - std::string m_path; - -public: - /** - * Create a Unix transport. - * - * \param path the path - */ - IRCCD_EXPORT TransportServerLocal(std::string path); - - /** - * Destroy the transport and remove the file. - */ - IRCCD_EXPORT ~TransportServerLocal(); -}; - -#endif // !_WIN32 - -} // !irccd - -#endif // !IRCCD_TRANSPORT_SERVER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/transport.cpp Fri Aug 12 15:34:03 2016 +0200 @@ -0,0 +1,311 @@ +/* + * transport.cpp -- irccd transports + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <cassert> +#include <cstdio> + +#include "transport.hpp" + +namespace irccd { + +/* + * TransportClient + * ------------------------------------------------------------------ + */ + +void TransportClient::parse(const std::string &message) +{ + auto document = nlohmann::json::parse(message); + + if (document.is_object()) + onCommand(document); +} + +unsigned TransportClient::recv(char *buffer, unsigned length) +{ + return m_socket.recv(buffer, length); +} + +unsigned TransportClient::send(const char *buffer, unsigned length) +{ + return m_socket.send(buffer, length); +} + +void TransportClient::syncInput() +{ + try { + std::string buffer; + + buffer.resize(512); + buffer.resize(recv(&buffer[0], buffer.size())); + + if (buffer.empty()) + onDie(); + + m_input += std::move(buffer); + } catch (const std::exception &) { + onDie(); + } +} + +void TransportClient::syncOutput() +{ + try { + auto ns = send(&m_output[0], m_output.size()); + + if (ns == 0) + onDie(); + + m_output.erase(0, ns); + } catch (const std::exception &ex) { + onDie(); + } +} + +void TransportClient::prepare(fd_set &in, fd_set &out, net::Handle &max) +{ + if (m_socket.handle() > max) + max = m_socket.handle(); + + FD_SET(m_socket.handle(), &in); + + if (!m_output.empty()) + FD_SET(m_socket.handle(), &out); +} + +void TransportClient::sync(fd_set &in, fd_set &out) +{ + // Do some I/O. + if (FD_ISSET(m_socket.handle(), &in)) + syncInput(); + if (FD_ISSET(m_socket.handle(), &out)) + syncOutput(); + + // Flush the queue. + for (std::size_t pos; (pos = m_input.find("\r\n\r\n")) != std::string::npos; ) { + auto message = m_input.substr(0, pos); + + m_input.erase(m_input.begin(), m_input.begin() + pos + 4); + + parse(message); + } +} + +void TransportClient::send(const nlohmann::json &json) +{ + assert(json.is_object()); + + m_output += json.dump(); + m_output += "\r\n\r\n"; +} + +/* + * TransportClientTls + * ------------------------------------------------------------------ + */ + +void TransportClientTls::handshake() +{ + try { + m_ssl.handshake(); + m_handshake = HandshakeReady; + } catch (const net::WantReadError &) { + m_handshake = HandshakeRead; + } catch (const net::WantWriteError &) { + m_handshake = HandshakeWrite; + } catch (const std::exception &) { + onDie(); + } +} + +TransportClientTls::TransportClientTls(const std::string &pkey, + const std::string &cert, + net::TcpSocket socket) + : TransportClient(std::move(socket)) + , m_ssl(m_socket) +{ + m_ssl.setPrivateKey(pkey); + m_ssl.setCertificate(cert); + + handshake(); +} + +unsigned TransportClientTls::recv(char *buffer, unsigned length) +{ + unsigned nread = 0; + + try { + nread = m_ssl.recv(buffer, length); + } catch (const net::WantReadError &) { + m_handshake = HandshakeRead; + } catch (const net::WantWriteError &) { + m_handshake = HandshakeWrite; + } + + return nread; +} + +unsigned TransportClientTls::send(const char *buffer, unsigned length) +{ + unsigned nsent = 0; + + try { + nsent = m_ssl.send(buffer, length); + } catch (const net::WantReadError &) { + m_handshake = HandshakeRead; + } catch (const net::WantWriteError &) { + m_handshake = HandshakeWrite; + } + + return nsent; +} + +void TransportClientTls::prepare(fd_set &in, fd_set &out, net::Handle &max) +{ + if (m_socket.handle() > max) + max = m_socket.handle(); + + switch (m_handshake) { + case HandshakeRead: + FD_SET(m_socket.handle(), &in); + break; + case HandshakeWrite: + FD_SET(m_socket.handle(), &out); + break; + default: + TransportClient::prepare(in, out, max); + break; + } +} + +void TransportClientTls::sync(fd_set &in, fd_set &out) +{ + switch (m_handshake) { + case HandshakeRead: + case HandshakeWrite: + handshake(); + break; + default: + TransportClient::sync(in, out); + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/* + * TransportServerIp + * ------------------------------------------------------------------ + */ + +TransportServerIp::TransportServerIp(const std::string &address, + std::uint16_t port, + std::uint8_t mode) + : TransportServer(net::TcpSocket((mode & v6) ? AF_INET6 : AF_INET, 0)) +{ + assert((mode & v6) || (mode & v4)); + + m_socket.set(net::option::SockReuseAddress(true)); + + if (mode & v6) { + if (address == "*") + m_socket.bind(net::ipv6::any(port)); + else + m_socket.bind(net::ipv6::pton(address, port)); + + // Disable or enable IPv4 when using IPv6. + if (!(mode & v4)) + m_socket.set(net::option::Ipv6Only(true)); + } else { + if (address == "*") + m_socket.bind(net::ipv4::any(port)); + else + m_socket.bind(net::ipv4::pton(address, port)); + } + + m_socket.listen(); +} + +/* + * TransportServerTls + * ------------------------------------------------------------------ + */ + +TransportServerTls::TransportServerTls(const std::string &pkey, + const std::string &cert, + const std::string &address, + std::uint16_t port, + std::uint8_t mode) + : TransportServerIp(address, port, mode) + , m_privatekey(pkey) + , m_cert(cert) +{ +} + +std::unique_ptr<TransportClient> TransportServerTls::accept() +{ + return std::make_unique<TransportClientTls>(m_privatekey, m_cert, m_socket.accept()); +} + +/* + * TransportServerLocal + * ------------------------------------------------------------------ + */ + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +TransportServerLocal::TransportServerLocal(std::string path) + : TransportServer(net::TcpSocket(AF_LOCAL, 0)) + , m_path(std::move(path)) +{ + m_socket.bind(net::local::create(m_path, true)); + m_socket.listen(); +} + +TransportServerLocal::~TransportServerLocal() +{ + ::remove(m_path.c_str()); +} + +#endif + +} // !irccd
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/irccd/transport.hpp Fri Aug 12 15:34:03 2016 +0200 @@ -0,0 +1,352 @@ +/* + * transport.hpp -- irccd transports + * + * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr> + * + * 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 appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_TRANSPORT_HPP +#define IRCCD_TRANSPORT_HPP + +/** + * \file transport.hpp + * \brief Irccd transports. + */ + +#include <cstdint> +#include <memory> +#include <string> + +#include <json.hpp> + +#include "net.hpp" +#include "signals.hpp" +#include "sysconfig.hpp" + +namespace irccd { + +/** + * \class TransportClient + * \brief Client connected to irccd. + * + * This class emits a warning upon clients request through onCommand signal. + */ +class TransportClient { +public: + /** + * Signal: onCommand + * ---------------------------------------------------------- + * + * Arguments: + * - the command + */ + Signal<const nlohmann::json &> onCommand; + + /** + * Signal: onDie + * ---------------------------------------------------------- + * + * The client has disconnected. + */ + Signal<> onDie; + +protected: + net::TcpSocket m_socket; //!< socket + std::string m_input; //!< input buffer + std::string m_output; //!< output buffer + + /** + * Parse input buffer. + * + * \param buffer the buffer. + */ + void parse(const std::string &buffer); + +protected: + /** + * Try to receive some data into the given buffer. + * + * \param buffer the destination buffer + * \param length the buffer length + * \return the number of bytes received + */ + IRCCD_EXPORT virtual unsigned recv(char *buffer, unsigned length); + + /** + * Try to send some data into the given buffer. + * + * \param buffer the source buffer + * \param length the buffer length + * \return the number of bytes sent + */ + IRCCD_EXPORT virtual unsigned send(const char *buffer, unsigned length); + +public: + /** + * Create a transport client from the socket. + * + * \pre socket must be valid + */ + inline TransportClient(net::TcpSocket socket) + : m_socket(std::move(socket)) + { + assert(m_socket.isOpen()); + + m_socket.set(net::option::SockBlockMode(false)); + } + + /** + * Virtual destructor defaulted. + */ + virtual ~TransportClient() = default; + + /** + * Convenient wrapper around recv(). + * + * Must be used in sync() function. + */ + IRCCD_EXPORT void syncInput(); + + /** + * Convenient wrapper around send(). + * + * Must be used in sync() function. + */ + IRCCD_EXPORT void syncOutput(); + + /** + * Append some data to the output queue. + * + * \pre json.is_object() + * \param json the json object + */ + IRCCD_EXPORT void send(const nlohmann::json &json); + + /** + * \copydoc Service::prepare + */ + IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max); + + /** + * \copydoc Service::sync + */ + IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out); +}; + +/* + * TransportClientTls + * ------------------------------------------------------------------ + */ + +/** + * \brief TLS version of transport client. + */ +class TransportClientTls : public TransportClient { +private: + enum { + HandshakeWrite, + HandshakeRead, + HandshakeReady + } m_handshake{HandshakeReady}; + + net::TlsSocket m_ssl; + + void handshake(); + +protected: + /** + * \copydoc TransportClient::recv + */ + unsigned recv(char *buffer, unsigned length) override; + + /** + * \copydoc TransportClient::send + */ + unsigned send(const char *buffer, unsigned length) override; + +public: + /** + * Create the transport client. + * + * \pre socket.isOpen() + * \param pkey the private key + * \param cert the certificate file + * \param socket the accepted socket + */ + IRCCD_EXPORT TransportClientTls(const std::string &pkey, + const std::string &cert, + net::TcpSocket socket); + + /** + * \copydoc TransportClient::prepare + */ + IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max); + + /** + * \copydoc TransportClient::sync + */ + IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out); +}; + +/* + * TransportServer + * ------------------------------------------------------------------ + */ + +/** + * \brief Bring networking between irccd and irccdctl. + * + * This class contains a master sockets for listening to TCP connections, it is then processed by irccd. + * + * The transport class supports the following domains: + * + * | Domain | Class | + * |-----------------------|-----------------------| + * | IPv4, IPv6 | TransportServerIp | + * | Unix (not on Windows) | TransportServerUnix | + * + * Note: IPv4 and IPv6 can be combined, using TransportServer::IPv6 and its option. + */ +class TransportServer { +private: + TransportServer(const TransportServer &) = delete; + TransportServer(TransportServer &&) = delete; + + TransportServer &operator=(const TransportServer &) = delete; + TransportServer &operator=(TransportServer &&) = delete; + +protected: + /** + * The socket handle. + */ + net::TcpSocket m_socket; + +public: + /** + * Default constructor. + */ + inline TransportServer(net::TcpSocket socket) + : m_socket(std::move(socket)) + { + } + + /** + * Get the socket handle for this transport. + * + * \return the handle + */ + inline net::Handle handle() const noexcept + { + return m_socket.handle(); + } + + /** + * Destructor defaulted. + */ + virtual ~TransportServer() = default; + + /** + * Accept a new client depending on the domain. + * + * \return the new client + */ + virtual std::unique_ptr<TransportClient> accept() + { + return std::make_unique<TransportClient>(m_socket.accept()); + } +}; + +/** + * \brief Create IP transport. + */ +class TransportServerIp : public TransportServer { +public: + /** + * \brief Domain to use. + */ + enum Mode { + v4 = (1 << 0), //!< IPv6 + v6 = (1 << 1) //!< IPv4 + }; + + /** + * Constructor. + * \pre mode > 0 + * \param address the address (* for any) + * \param port the port number + * \param mode the domains to use (can be OR'ed) + */ + IRCCD_EXPORT TransportServerIp(const std::string &address, + std::uint16_t port, + std::uint8_t mode = v4); +}; + +/** + * \brief TLS over IP transport. + */ +class TransportServerTls : public TransportServerIp { +private: + std::string m_privatekey; + std::string m_cert; + +public: + /** + * Constructor. + * \pre mode > 0 + * \param pkey the private key file + * \param cert the certificate file + * \param address the address (* for any) + * \param port the port number + * \param mode the domains to use (can be OR'ed) + */ + IRCCD_EXPORT TransportServerTls(const std::string &pkey, + const std::string &cert, + const std::string &address, + std::uint16_t port, + std::uint8_t mode = v4); + + /** + * \copydoc TransportServer::accept + */ + IRCCD_EXPORT std::unique_ptr<TransportClient> accept() override; +}; + +#if !defined(IRCCD_SYSTEM_WINDOWS) + +/** + * \brief Implementation of transports for Unix sockets. + */ +class TransportServerLocal : public TransportServer { +private: + std::string m_path; + +public: + /** + * Create a Unix transport. + * + * \param path the path + */ + IRCCD_EXPORT TransportServerLocal(std::string path); + + /** + * Destroy the transport and remove the file. + */ + IRCCD_EXPORT ~TransportServerLocal(); +}; + +#endif // !IRCCD_SYSTEM_WINDOWS + +} // !irccd + +#endif // !IRCCD_TRANSPORT_HPP