Mercurial > irccd
changeset 234:3c631fb06ccf
Irccd: support SSL transport part 1
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 12 Aug 2016 13:16:59 +0200 |
parents | f8094e852dd5 |
children | 5566cf772073 |
files | lib/irccd/config.cpp lib/irccd/net.hpp lib/irccd/transport-client.cpp lib/irccd/transport-client.hpp lib/irccd/transport-server.cpp lib/irccd/transport-server.hpp |
diffstat | 6 files changed, 202 insertions(+), 13 deletions(-) [+] |
line wrap: on
line diff
--- a/lib/irccd/config.cpp Thu Aug 11 12:05:26 2016 +0200 +++ b/lib/irccd/config.cpp Fri Aug 12 13:16:59 2016 +0200 @@ -171,10 +171,29 @@ } } + // Optional SSL. + std::string pkey; + std::string cert; + + if ((it = sc.find("ssl")) != sc.end() && util::isBoolean(it->value())) { + if ((it = sc.find("certificate")) == sc.end()) + throw std::invalid_argument("transport: missing 'certificate' parameter"); + + cert = it->value(); + + if ((it = sc.find("key")) == sc.end()) + throw std::invalid_argument("transport: missing 'key' parameter"); + + pkey = it->value(); + } + if (mode == 0) throw std::invalid_argument("transport: domain must at least have ipv4 or ipv6"); - return std::make_shared<TransportServerIp>(address, port, mode); + if (pkey.empty()) + return std::make_shared<TransportServerIp>(address, port, mode); + + return std::make_shared<TransportServerTls>(pkey, cert, address, port, mode); } std::shared_ptr<TransportServer> loadTransportUnix(const ini::Section &sc)
--- a/lib/irccd/net.hpp Thu Aug 11 12:05:26 2016 +0200 +++ b/lib/irccd/net.hpp Fri Aug 12 13:16:59 2016 +0200 @@ -2203,7 +2203,7 @@ */ void handshake() { - wrap([&] () -> int { + wrap([this] () -> int { return SSL_do_handshake(m_ssl.get()); }); } @@ -2221,7 +2221,7 @@ int nbread = 0; wrap([&] () -> int { - return (nbread = SSL_read(m_ssl.get(), data, max)); + return nbread = SSL_read(m_ssl.get(), data, max); }); return static_cast<unsigned>(nbread < 0 ? 0 : nbread); @@ -2240,7 +2240,7 @@ int nbsent = 0; wrap([&] () -> int { - return (nbsent = SSL_write(m_ssl.get(), data, max)); + return nbsent = SSL_write(m_ssl.get(), data, max); }); return static_cast<unsigned>(nbsent < 0 ? 0 : nbsent); @@ -2497,7 +2497,7 @@ length = sizeof (sun); #endif - return Address(reinterpret_cast<const sockaddr *>(&sun), length); + return Address(reinterpret_cast<const sockaddr *>(&sun), length); } /**
--- a/lib/irccd/transport-client.cpp Thu Aug 11 12:05:26 2016 +0200 +++ b/lib/irccd/transport-client.cpp Fri Aug 12 13:16:59 2016 +0200 @@ -30,7 +30,7 @@ onCommand(document); } -void TransportClient::receive() +void TransportClient::syncInput() { try { std::string buffer; @@ -47,7 +47,7 @@ } } -void TransportClient::send() +void TransportClient::syncOutput() { try { auto ns = m_socket.send(&m_output[0], m_output.size()); @@ -77,11 +77,11 @@ // Do some I/O. if (FD_ISSET(m_socket.handle(), &in)) { log::debug() << "transport: receiving to input buffer" << std::endl; - receive(); + syncInput(); } if (FD_ISSET(m_socket.handle(), &out)) { log::debug() << "transport: sending outgoing buffer" << std::endl; - send(); + syncOutput(); } // Flush the queue. @@ -102,4 +102,96 @@ 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(); +} + +void TransportClientTls::syncInput() +{ + try { + std::string buffer; + + buffer.resize(512); + buffer.resize(m_ssl.recv(&buffer[0], buffer.size())); + + if (buffer.empty()) + onDie(); + + m_input += std::move(buffer); + } catch (const std::exception &) { + onDie(); + } +} + +void TransportClientTls::syncOutput() +{ + try { + auto ns = m_ssl.send(&m_output[0], m_output.size()); + + if (ns == 0) + onDie(); + + m_output.erase(0, ns); + } catch (const std::exception &ex) { + onDie(); + } +} + +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 Thu Aug 11 12:05:26 2016 +0200 +++ b/lib/irccd/transport-client.hpp Fri Aug 12 13:16:59 2016 +0200 @@ -75,14 +75,15 @@ */ void parse(const std::string &buffer); -private: - void receive(); - void send(); +protected: + virtual void syncInput(); + virtual void syncOutput(); public: inline TransportClient(net::TcpSocket socket) : m_socket(std::move(socket)) { + m_socket.set(net::option::SockBlockMode(false)); } /** @@ -97,6 +98,32 @@ 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: + void syncInput() override; + void syncOutput() 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 Thu Aug 11 12:05:26 2016 +0200 +++ b/lib/irccd/transport-server.cpp Fri Aug 12 13:16:59 2016 +0200 @@ -30,7 +30,7 @@ namespace irccd { /* - * TransportServerIpv6 + * TransportServerIp * ------------------------------------------------------------------ */ @@ -65,6 +65,29 @@ } /* + * 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 * ------------------------------------------------------------------ */
--- a/lib/irccd/transport-server.hpp Thu Aug 11 12:05:26 2016 +0200 +++ b/lib/irccd/transport-server.hpp Fri Aug 12 13:16:59 2016 +0200 @@ -121,6 +121,34 @@ 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 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) /**