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