From 8e08542ff49ff6eb68085bab599cfb467b3967dd Mon Sep 17 00:00:00 2001 From: mrabine Date: Fri, 29 May 2026 22:19:56 +0200 Subject: [PATCH 1/9] Decouple TLS from transport --- core/include/join/acceptor.hpp | 9 +- core/include/join/openssl.hpp | 2 +- core/include/join/socket.hpp | 12 +- core/tests/openssl_test.cpp | 2 +- crypto/CMakeLists.txt | 5 + crypto/include/join/tlscontext.hpp | 203 +++++ crypto/include/join/tlserror.hpp | 92 +++ crypto/include/join/tlswrapper.hpp | 1187 ++++++++++++++++++++++++++++ crypto/src/tlscontext.cpp | 367 +++++++++ crypto/src/tlserror.cpp | 83 ++ crypto/tests/CMakeLists.txt | 20 + crypto/tests/dtlswrapper_test.cpp | 1032 ++++++++++++++++++++++++ crypto/tests/tlscontext_test.cpp | 418 ++++++++++ crypto/tests/tlserror_test.cpp | 150 ++++ crypto/tests/tlswrapper_test.cpp | 1036 ++++++++++++++++++++++++ 15 files changed, 4609 insertions(+), 9 deletions(-) create mode 100644 crypto/include/join/tlscontext.hpp create mode 100644 crypto/include/join/tlserror.hpp create mode 100644 crypto/include/join/tlswrapper.hpp create mode 100644 crypto/src/tlscontext.cpp create mode 100644 crypto/src/tlserror.cpp create mode 100644 crypto/tests/dtlswrapper_test.cpp create mode 100644 crypto/tests/tlscontext_test.cpp create mode 100644 crypto/tests/tlserror_test.cpp create mode 100644 crypto/tests/tlswrapper_test.cpp diff --git a/core/include/join/acceptor.hpp b/core/include/join/acceptor.hpp index 23ad597e..3a253e17 100644 --- a/core/include/join/acceptor.hpp +++ b/core/include/join/acceptor.hpp @@ -301,7 +301,7 @@ namespace join */ BasicTlsAcceptor () : BasicStreamAcceptor () - , _tlsContext (SSL_CTX_new (TLS_server_method ())) + , _tlsContext (SSL_CTX_new (TLS_server_method ()), SslCtxDelete ()) , _sessionId (randomize ()) { // enable the OpenSSL bug workaround options. @@ -409,10 +409,10 @@ namespace join { struct sockaddr_storage sa; socklen_t sa_len = sizeof (struct sockaddr_storage); - Socket sock (join::SslCtxPtr (this->_tlsContext.get ())); - SSL_CTX_up_ref (this->_tlsContext.get ()); + Socket sock (this->_tlsContext); - sock._handle = ::accept (this->_handle, reinterpret_cast (&sa), &sa_len); + sock._handle = ::accept4 (this->_handle, reinterpret_cast (&sa), &sa_len, + SOCK_NONBLOCK | SOCK_CLOEXEC); if (sock._handle == -1) { lastError = std::error_code (errno, std::generic_category ()); @@ -423,7 +423,6 @@ namespace join sock._state = Socket::Connected; sock.setOption (Socket::NoDelay, 1); - sock.setMode (Socket::NonBlocking); return sock; } diff --git a/core/include/join/openssl.hpp b/core/include/join/openssl.hpp index fb615696..baf4628e 100644 --- a/core/include/join/openssl.hpp +++ b/core/include/join/openssl.hpp @@ -237,7 +237,7 @@ namespace join } }; - using SslCtxPtr = std::unique_ptr; + using SslCtxPtr = std::shared_ptr; #if OPENSSL_VERSION_NUMBER < 0x30000000L /** diff --git a/core/include/join/socket.hpp b/core/include/join/socket.hpp index 4d401318..c89e3296 100644 --- a/core/include/join/socket.hpp +++ b/core/include/join/socket.hpp @@ -59,6 +59,7 @@ namespace join { public: using Ptr = std::unique_ptr>; + using Proto = Protocol; using Endpoint = typename Protocol::Endpoint; /** @@ -622,6 +623,10 @@ namespace join /// protocol. Protocol _protocol; + + /// friendship with TLS wrapper. + template + friend class TlsWrapper; }; /** @@ -644,6 +649,7 @@ namespace join { public: using Ptr = std::unique_ptr>; + using Proto = Protocol; using Mode = typename BasicSocket::Mode; using Option = typename BasicSocket::Option; using State = typename BasicSocket::State; @@ -1141,6 +1147,7 @@ namespace join { public: using Ptr = std::unique_ptr>; + using Proto = Protocol; using Mode = typename BasicDatagramSocket::Mode; using Option = typename BasicDatagramSocket::Option; using State = typename BasicDatagramSocket::State; @@ -1264,7 +1271,7 @@ namespace join this->_state = State::Disconnected; } - this->close (); + // this->close (); return 0; } @@ -1551,6 +1558,7 @@ namespace join { public: using Ptr = std::unique_ptr>; + using Proto = Protocol; using Mode = typename BasicStreamSocket::Mode; using Option = typename BasicStreamSocket::Option; using State = typename BasicStreamSocket::State; @@ -1570,7 +1578,7 @@ namespace join */ BasicTlsSocket (Mode mode) : BasicStreamSocket (mode) - , _tlsContext (SSL_CTX_new (TLS_client_method ())) + , _tlsContext (SSL_CTX_new (TLS_client_method ()), SslCtxDelete ()) { // enable the OpenSSL bug workaround options. SSL_CTX_set_options (this->_tlsContext.get (), SSL_OP_ALL); diff --git a/core/tests/openssl_test.cpp b/core/tests/openssl_test.cpp index 50c9ca32..70003108 100644 --- a/core/tests/openssl_test.cpp +++ b/core/tests/openssl_test.cpp @@ -258,7 +258,7 @@ TEST_F (Openssl, StackOfGeneralNamePtr) */ TEST_F (Openssl, SslPtr) { - join::SslCtxPtr ctx (SSL_CTX_new (TLS_method ())); + join::SslCtxPtr ctx (SSL_CTX_new (TLS_method ()), join::SslCtxDelete ()); ASSERT_NE (ctx, nullptr); join::SslPtr ssl (SSL_new (ctx.get ())); ASSERT_NE (ssl, nullptr); diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 8e83e61f..7d901561 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -6,6 +6,9 @@ set(PUBLIC_HEADERS include/join/hmac.hpp include/join/tlskey.hpp include/join/signature.hpp + # include/join/tlserror.hpp + include/join/tlscontext.hpp + include/join/tlswrapper.hpp ) set(SOURCES @@ -14,6 +17,8 @@ set(SOURCES src/hmac.cpp src/tlskey.cpp src/signature.cpp + # src/tlserror.cpp + src/tlscontext.cpp ) add_library(${JOIN_CRYPTO} ${SOURCES}) diff --git a/crypto/include/join/tlscontext.hpp b/crypto/include/join/tlscontext.hpp new file mode 100644 index 00000000..69a7f150 --- /dev/null +++ b/crypto/include/join/tlscontext.hpp @@ -0,0 +1,203 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_CORE_TLS_CONTEXT_HPP +#define JOIN_CORE_TLS_CONTEXT_HPP + +// libjoin. +// #include +#include + +namespace join +{ + /** + * @brief TLS/DTLS context. + */ + class TlsContext + { + public: + /** + * @brief TLS/DTLS role. + */ + enum class Role + { + TlsClient, /**< TLS client. */ + TlsServer, /**< TLS server. */ + DtlsClient, /**< DTLS client. */ + DtlsServer, /**< DTLS server. */ + }; + + /** + * @brief create TLS/DTLS context for the given role. + * @param role client or server, TLS or DTLS. + * @throw std::runtime_error if SSL_CTX_new fails. + */ + explicit TlsContext (Role role = Role::TlsClient); + + /** + * @brief copy constructor. + * @param other other object to copy. + */ + TlsContext (const TlsContext&) = default; + + /** + * @brief copy assignment operator. + * @param other other object to assign. + * @return assigned object. + */ + TlsContext& operator= (const TlsContext&) = default; + + /** + * @brief move constructor. + * @param other other object to move. + */ + TlsContext (TlsContext&&) = default; + + /** + * @brief move assignment operator. + * @param other other object to assign. + * @return assigned object. + */ + TlsContext& operator= (TlsContext&&) = default; + + /** + * @brief destroy instance. + */ + ~TlsContext () = default; + + /** + * @brief set the certificate and the private key. + * @param cert certificate path. + * @param key private key path. + * @return 0 on success, -1 on failure. + */ + int setCertificate (const std::string& cert, const std::string& key = "") noexcept; + + /** + * @brief set the location of the trusted CA certificates. + * @param caPath path of the trusted CA certificates. + * @return 0 on success, -1 on failure. + */ + int setCaPath (const std::string& caPath); + + /** + * @brief set the location of the trusted CA certificate file. + * @param caFile path of the trusted CA certificate file. + * @return 0 on success, -1 on failure. + */ + int setCaFile (const std::string& caFile); + + /** + * @brief enable or disable peer certificate verification. + * @param verify true to enable peer verification. + * @param depth maximum certificate chain depth (-1 = no limit). + */ + void setVerify (bool verify, int depth = -1) noexcept; + + /** + * @brief set the cipher list (TLSv1.2 and below). + * @param cipher the cipher list. + * @return 0 on success, -1 on failure. + */ + int setCipher (const std::string& cipher) noexcept; + + /** + * @brief set the cipher list (TLSv1.3). + * @param cipher the cipher list. + * @return 0 on success, -1 on failure. + */ + int setCipher_1_3 (const std::string& cipher) noexcept; + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + /** + * @brief set elliptic curve list (OpenSSL 3+). + * @param curves curve list string. + * @return 0 on success, -1 on failure. + */ + int setCurve (const std::string& curves); +#endif + + /** + * @brief set the ALPN protocols list. + * @param protocols list of protocol names (e.g. {"h2", "http/1.1"}). + * @return 0 on success, -1 on failure. + */ + int setAlpnProtocols (const std::vector& protocols); + + /** + * @brief get the native SSL_CTX handle. + * @return SSL_CTX*. + */ + SSL_CTX* handle () const noexcept; + + /** + * @brief check if peer verification is enabled. + * @return true if enabled. + */ + bool verify () const noexcept; + + /** + * @brief get the maximum certificate chain depth. + * @return depth. + */ + int depth () const noexcept; + + /** + * @brief check if the role is a server role. + * @return true if the role is a server role, false otherwise. + */ + bool isServer () const noexcept; + + private: + /** + * @brief get the SSL_METHOD for the given role. + * @param role client or server, TLS or DTLS. + * @return the SSL_METHOD for the given role. + */ + static const SSL_METHOD* method (Role role) noexcept; + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + /** + * @brief generate openssl Diffie-Hellman parameters. + * @note random Diffie-Hellman parameters generated using the command "openssl dhparam -C 2236". + * @return Diffie-Hellman parameters. + */ + static DH* getDh2236 (); +#endif + + /// TLS role. + Role _role; + + /// peer verification enabled. + bool _verify = false; + + /// maximum certificate chain depth. + int _depth = -1; + + /// OpenSSL context. + SslCtxPtr _ctx; + }; +} + +#endif diff --git a/crypto/include/join/tlserror.hpp b/crypto/include/join/tlserror.hpp new file mode 100644 index 00000000..32b08990 --- /dev/null +++ b/crypto/include/join/tlserror.hpp @@ -0,0 +1,92 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_CORE_TLS_ERROR_HPP +#define JOIN_CORE_TLS_ERROR_HPP + +// C++. +#include + +namespace join +{ + /** + * @brief TLS error codes. + */ + enum class TlsErrc + { + TlsCloseNotifyAlert = 1, /**< A close notify alert was received. */ + TlsProtocolError /**< A failure in the TLS library occurred, usually a protocol error. */ + }; + + /** + * @brief TLS error category. + */ + class TlsCategory : public std::error_category + { + public: + /** + * @brief get digest error category name. + * @return digest error category name. + */ + virtual const char* name () const noexcept; + + /** + * @brief translate digest error code to human readable error string. + * @param code error code. + * @return human readable error string. + */ + virtual std::string message (int code) const; + }; + + /** + * @brief get error category. + * @return the created std::error_category object. + */ + const std::error_category& getTlsCategory (); + + /** + * @brief create an std::error_code object. + * @param code error code number. + * @return the created std::error_code object. + */ + std::error_code make_error_code (TlsErrc code); + + /** + * @brief create an std::error_condition object. + * @param code error code number. + * @return the created std::error_condition object. + */ + std::error_condition make_error_condition (TlsErrc code); +} + +namespace std +{ + /// TLS error code specialization. + template <> + struct is_error_condition_enum : public true_type + { + }; +} + +#endif diff --git a/crypto/include/join/tlswrapper.hpp b/crypto/include/join/tlswrapper.hpp new file mode 100644 index 00000000..285c7c69 --- /dev/null +++ b/crypto/include/join/tlswrapper.hpp @@ -0,0 +1,1187 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_CORE_TLS_WRAPPER_HPP +#define JOIN_CORE_TLS_WRAPPER_HPP + +// libjoin. +#include +#include +#include + +// C. +#include + +namespace join +{ + /** + * @brief TLS/DTLS decorator. + */ + template + class TlsWrapper + { + public: + using Protocol = typename Socket::Proto; + using Mode = typename Socket::Mode; + using Option = typename Socket::Option; + using State = typename Socket::State; + using Endpoint = typename Socket::Endpoint; + + /** + * @brief create a TLS wrapper with an internally created socket. + * @param ctx TLS context. + */ + explicit TlsWrapper (TlsContext ctx, Mode mode = Socket::Mode::NonBlocking) noexcept + : _socket (mode) + , _ctx (ctx) + { + } + + /** + * @brief create a TLS wrapper taking ownership of the given socket. + * @param socket underlying socket. + * @param ctx TLS context. + */ + TlsWrapper (Socket&& socket, TlsContext ctx) noexcept + : _socket (std::move (socket)) + , _ctx (ctx) + { + } + + /** + * @brief copy constructor. + * @param other other object to copy. + */ + TlsWrapper (const TlsWrapper&) = delete; + + /** + * @brief copy assignment operator. + * @param other other object to assign. + * @return assigned object. + */ + TlsWrapper& operator= (const TlsWrapper&) = delete; + + /** + * @brief move constructor. + * @param other other object to move. + */ + TlsWrapper (TlsWrapper&& other) noexcept + : _socket (std::move (other._socket)) + , _ctx (std::move (other._ctx)) + , _ssl (std::move (other._ssl)) + { + if (_ssl) + { + SSL_set_app_data (_ssl.get (), this); + } + } + + /** + * @brief move assignment operator. + * @param other other object to assign. + * @return assigned object. + */ + TlsWrapper& operator= (TlsWrapper&& other) noexcept + { + _socket = std::move (other._socket); + _ctx = std::move (other._ctx); + _ssl = std::move (other._ssl); + + if (_ssl) + { + SSL_set_app_data (_ssl.get (), this); + } + + return *this; + } + + /** + * @brief destroy TLS stream. + */ + ~TlsWrapper () = default; + + /** + * @brief open the underlying socket using the given protocol. + * @param protocol protocol to use. + * @return 0 on success, -1 on failure. + */ + int open (const Protocol& protocol = Protocol ()) noexcept + { + return _socket.open (protocol); + } + + /** + * @brief check if the underlying socket is opened. + * @return true if opened. + */ + bool opened () const noexcept + { + return _socket.opened (); + } + + /** + * @brief assigns the specified endpoint to the underlying socket. + * @param endpoint endpoint to assign to the underlying socket. + * @return 0 on success, -1 on failure. + */ + int bind (const Endpoint& ep) noexcept + { + return _socket.bind (ep); + } + + /** + * @brief assigns the specified device to the underlying socket. + * @param device device name. + * @return 0 on success, -1 on failure. + */ + int bindToDevice (const std::string& dev) noexcept + { + return _socket.bindToDevice (dev); + } + + /** + * @brief connect the the underlying socket to the remote endpoint. + * @param endpoint endpoint to connect to. + * @return 0 on success, -1 on failure. + */ + int connect (const Endpoint& ep) + { + return _socket.connect (ep); + } + + /** + * @brief check if the underlying socket is connecting. + * @return true if connecting. + */ + bool connecting () const noexcept + { + return _socket.connecting (); + } + + /** + * @brief block until the underlying socket is connected. + * @param timeout timeout in milliseconds. + * @return true if connected, false otherwise. + */ + template ::type = 0> + bool waitConnected (int timeout = 0) + { + return _socket.waitConnected (timeout); + } + + /** + * @brief block until the underlying socket is connected. + * @param timeout timeout in milliseconds. + * @return true if connected, false otherwise. + */ + template ::type = 0> + bool waitConnected ([[maybe_unused]] int timeout = 0) + { + if (_ctx.isServer ()) + { + return true; + } + + return _socket.connected (); + } + + /** + * @brief check if the underlying socket is connected. + * @return true if connected. + */ + bool connected () noexcept + { + return _socket.connected (); + } + + /** + * @brief Perform the TLS handshake. + * @return 0 on success, -1 on failure. + */ + int handshake () noexcept + { + if (encrypted ()) + { + return 0; + } + + if (!_ssl) + { + if (_socket.type () == SOCK_DGRAM) + { + if (!_socket.opened ()) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + if (!_ctx.isServer () && !_socket.connected ()) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + } + + if (_socket.type () == SOCK_STREAM) + { + if (!_socket.connected ()) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + } + + _ssl.reset (SSL_new (_ctx.handle ())); + if (!_ssl) + { + // LCOV_EXCL_START + lastError = make_error_code (Errc::OutOfMemory); + return -1; + // LCOV_EXCL_STOP + } + + if (_socket.type () == SOCK_DGRAM) + { + BIO* bio = BIO_new_dgram (_socket.handle (), BIO_NOCLOSE); + if (!bio) + { + lastError = make_error_code (Errc::OutOfMemory); + _ssl.reset (); + return -1; + } + + if (_socket.connected ()) + { + BIO_ctrl (bio, BIO_CTRL_DGRAM_SET_CONNECTED, 0, + const_cast (_socket.remoteEndpoint ().addr ())); + } + + SSL_set_bio (_ssl.get (), bio, bio); + SSL_set_read_ahead (_ssl.get (), 1); + } + else + { + if (SSL_set_fd (_ssl.get (), _socket.handle ()) == 0) + { + lastError = make_error_code (Errc::InvalidParam); + _ssl.reset (); + return -1; + } + } + + if (SSL_is_server (_ssl.get ())) + { + SSL_set_accept_state (_ssl.get ()); + } + else + { + const std::string& host = _socket.remoteEndpoint ().hostname (); + if (!host.empty () && SSL_set_tlsext_host_name (_ssl.get (), host.c_str ()) != 1) + { + lastError = make_error_code (Errc::InvalidParam); + _ssl.reset (); + return -1; + } + + SSL_set_connect_state (_ssl.get ()); + } + + SSL_set_app_data (_ssl.get (), this); +#ifdef DEBUG + SSL_set_info_callback (_ssl.get (), infoWrapper); +#endif + + if (_ctx.verify ()) + { + SSL_set_verify (_ssl.get (), SSL_VERIFY_PEER, verifyWrapper); + SSL_set_verify_depth (_ssl.get (), _ctx.depth ()); + } + else + { + SSL_set_verify (_ssl.get (), SSL_VERIFY_NONE, nullptr); + } + } + + int result = SSL_do_handshake (_ssl.get ()); + if (result < 1) + { + return handleTlsError (result); + } + + return 0; + } + + /** + * @brief block until TLS handshake is finnished. + * @param timeout timeout in milliseconds (0: infinite). + * @return true if TLS handshake is finnished. + */ + bool waitHandshake (int timeout) noexcept + { + if (!waitConnected (timeout)) + { + return false; + } + + if (handshake () == 0) + { + return true; + } + + const bool isDtls = (_socket.type () == SOCK_DGRAM); + + while (lastError == make_error_code (Errc::TemporaryError)) + { + bool wantRead = SSL_want_read (_ssl.get ()); + bool wantWrite = SSL_want_write (_ssl.get ()); + + if (!wantRead && !wantWrite) + { + break; // LCOV_EXCL_LINE + } + + int activeTimeout = timeout; + + if (isDtls) + { + struct timeval dtlsTimeout; + if (DTLSv1_get_timeout (_ssl.get (), &dtlsTimeout)) + { + int dtlsTimeoutMs = (dtlsTimeout.tv_sec * 1000) + (dtlsTimeout.tv_usec / 1000); + if (timeout <= 0 || dtlsTimeoutMs < timeout) + { + activeTimeout = dtlsTimeoutMs; + } + } + } + + int waitResult = _socket.wait (wantRead, wantWrite, activeTimeout); + if (waitResult == -1) + { + if (isDtls && (lastError == make_error_code (Errc::TimedOut))) + { + int ret = DTLSv1_handle_timeout (_ssl.get ()); + if (ret < 0) + { + lastError = make_error_code (TlsErrc::TlsProtocolError); + return false; + } + + lastError = make_error_code (Errc::TemporaryError); + if (handshake () == 0) + { + return true; + } + + continue; + } + + return false; + } + + if (handshake () == 0) + { + return true; + } + } + + return false; + } + + /** + * @brief check if the stream is encrypted. + * @return true if encrypted. + */ + bool encrypted () const noexcept + { + return _ssl != nullptr && SSL_is_init_finished (_ssl.get ()); + } + + /** + * @brief Perform the TLS shutdown. + * @return 0 on success, -1 on failure. + */ + int shutdown () noexcept + { + if (!_ssl) + { + return 0; + } + + if ((SSL_get_shutdown (_ssl.get ()) & SSL_SENT_SHUTDOWN) == 0) + { + int result = SSL_shutdown (_ssl.get ()); + if (result < 0) + { + return handleTlsError (result); + } + } + + if (_socket.type () == SOCK_STREAM) + { + _ssl.reset (); + return 0; + } + + if ((SSL_get_shutdown (_ssl.get ()) & SSL_RECEIVED_SHUTDOWN) == 0) + { + int result = SSL_shutdown (_ssl.get ()); + if (result < 0) + { + return handleTlsError (result); + } + } + + _ssl.reset (); + return 0; + } + + /** + * @brief block until TLS shutdown is finnished. + * @param timeout timeout in milliseconds (0: infinite). + * @return true if TLS shutdown is finnished. + */ + bool waitShutdown (int timeout) noexcept + { + if (!_ssl) + { + return true; + } + + if (shutdown () == 0) + { + return true; + } + + while (lastError == make_error_code (Errc::TemporaryError)) + { + bool wantRead = SSL_want_read (_ssl.get ()); + bool wantWrite = SSL_want_write (_ssl.get ()); + + if (!wantRead && !wantWrite) + { + break; // LCOV_EXCL_LINE + } + + if (_socket.wait (wantRead, wantWrite, timeout) == -1) + { + return false; + } + + if (shutdown () == 0) + { + return true; + } + } + + return false; + } + + /** + * @brief disconnect the underlying socket from the remote endpoint. + * @return 0 on success, -1 on failure. + */ + int disconnect () + { + return _socket.disconnect (); + } + + /** + * @brief block until the underlying socket is disconnected. + * @param timeout timeout in milliseconds. + * @return true if disconnected, false otherwise. + */ + template ::type = 0> + bool waitDisconnected (int timeout = 0) + { + return _socket.waitDisconnected (timeout); + } + + /** + * @brief block until the underlying socket is disconnected. + * @param timeout timeout in milliseconds. + * @return true if disconnected, false otherwise. + */ + template ::type = 0> + bool waitDisconnected ([[maybe_unused]] int timeout) + { + if (_ctx.isServer ()) + { + return true; + } + + return !_socket.connected (); + } + + /** + * @brief close the socket handle. + */ + void close () noexcept + { + _ssl.reset (); + _socket.close (); + } + + /** + * @brief block until new data is available for reading. + * @param timeout timeout in milliseconds (0: infinite). + * @return true if there is new data available for reading, false otherwise. + */ + bool waitReadyRead (int timeout = 0) const noexcept + { + if (encrypted ()) + { + bool wantRead = SSL_want_read (_ssl.get ()); + bool wantWrite = SSL_want_write (_ssl.get ()); + + if (wantRead || wantWrite) + { + return (_socket.wait (wantRead, wantWrite, timeout) == 0); + } + } + + return _socket.waitReadyRead (timeout); + } + + /** + * @brief read data on the socket. + * @param buf buffer used to store the data received. + * @param len maximum number of bytes to read. + * @param endpoint endpoint from where data are coming (optional). + * @return The number of bytes received, -1 on failure. + */ + int readFrom (char* buf, unsigned long len, Endpoint* endpoint = nullptr) noexcept + { + if (encrypted ()) + { + int nread = SSL_read (_ssl.get (), buf, static_cast (len)); + if (nread < 1) + { + return handleTlsError (nread); + } + + if (endpoint != nullptr) + { + BIO* rbio = SSL_get_rbio (_ssl.get ()); + if (!rbio) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + struct sockaddr_storage sa; + socklen_t sa_len = sizeof (struct sockaddr_storage); + if (BIO_dgram_get_peer (rbio, &sa) <= 0) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + *endpoint = Endpoint (reinterpret_cast (&sa), sa_len); + } + + return nread; + } + + return _socket.readFrom (buf, len, endpoint); + } + + /** + * @brief read data from the TLS stream. + * @param buf buffer to read into. + * @param len maximum number of bytes to read. + * @return number of bytes read on success, -1 on failure. + */ + int read (char* buf, unsigned long len) noexcept + { + if (encrypted ()) + { + int nread = SSL_read (_ssl.get (), buf, static_cast (len)); + if (nread < 1) + { + return handleTlsError (nread); + } + + return nread; + } + + return _socket.read (buf, len); + } + + /** + * @brief read data until size is reached or an error occurred. + * @param data buffer used to store the data received. + * @param size number of bytes to read. + * @param timeout timeout in milliseconds. + * @return 0 on success, -1 on failure. + */ + int readExactly (char* data, unsigned long size, int timeout = 0) + { + unsigned long numRead = 0; + + while (numRead < size) + { + int result = read (data + numRead, size - numRead); + if (result == -1) + { + if (lastError == Errc::TemporaryError) + { + if (waitReadyRead (timeout)) + continue; + } + return -1; + } + + numRead += result; + } + + return 0; + } + + /** + * @brief block until until at least one byte can be written on the socket. + * @param timeout timeout in milliseconds (0: infinite). + * @return true if data can be written on the socket, false otherwise. + */ + bool waitReadyWrite (int timeout = 0) const noexcept + { + if (encrypted ()) + { + bool wantRead = SSL_want_read (_ssl.get ()); + bool wantWrite = SSL_want_write (_ssl.get ()); + + if (wantRead || wantWrite) + { + return (_socket.wait (wantRead, wantWrite, timeout) == 0); + } + } + + return _socket.waitReadyWrite (timeout); + } + + /** + * @brief write data on the socket. + * @param buf buffer to write from. + * @param len number of bytes to write. + * @param endpoint endpoint where to write the data. + * @return the number of bytes written, -1 on failure. + */ + int writeTo (const char* buf, unsigned long len, const Endpoint& endpoint) noexcept + { + if (encrypted ()) + { + BIO* wbio = SSL_get_wbio (_ssl.get ()); + if (!wbio) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + struct sockaddr_storage sa; + socklen_t sa_len = sizeof (struct sockaddr_storage); + if (BIO_dgram_get_peer (wbio, &sa) <= 0) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + Endpoint remote (reinterpret_cast (&sa), sa_len); + if (remote != endpoint) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + int nwritten = SSL_write (_ssl.get (), buf, static_cast (len)); + if (nwritten < 1) + { + return handleTlsError (nwritten); + } + + return nwritten; + } + + return _socket.writeTo (buf, len, endpoint); + } + + /** + * @brief write data to the TLS stream. + * @param buf buffer to write from. + * @param len number of bytes to write. + * @return number of bytes written on success, -1 on failure. + */ + int write (const char* buf, unsigned long len) noexcept + { + if (encrypted ()) + { + int nwritten = SSL_write (_ssl.get (), buf, static_cast (len)); + if (nwritten < 1) + { + return handleTlsError (nwritten); + } + + return nwritten; + } + + return _socket.write (buf, len); + } + + /** + * @brief write data until size is reached or an error occurred. + * @param data data buffer to send. + * @param size number of bytes to write. + * @param timeout timeout in milliseconds. + * @return 0 on success, -1 on failure. + */ + int writeExactly (const char* data, unsigned long size, int timeout = 0) + { + unsigned long numWrite = 0; + + while (numWrite < size) + { + int result = write (data + numWrite, size - numWrite); + if (result == -1) + { + if (lastError == Errc::TemporaryError) + { + if (waitReadyWrite (timeout)) + continue; + } + return -1; + } + + numWrite += result; + } + + return 0; + } + + /** + * @brief set the mode of the underlying socket. + * @param mode socket mode. + */ + void setMode (Mode mode) noexcept + { + _socket.setMode (mode); + } + + /** + * @brief set an option for the underlying socket. + * @param opt socket option. + * @param val option value. + * @return 0 on success, -1 on failure. + */ + int setOption (typename Socket::Option opt, int val) noexcept + { + return _socket.setOption (opt, val); + } + + /** + * @brief get the underlying socket handle. + * @return socket handle. + */ + int handle () const noexcept + { + return _socket.handle (); + } + + /** + * @brief get the underlying socket address family. + * @return socket address family. + */ + int family () const noexcept + { + return _socket.family (); + } + + /** + * @brief get the underlying socket type. + * @return socket type. + */ + int type () const noexcept + { + return _socket.type (); + } + + /** + * @brief get the underlying protocol. + * @return protocol. + */ + int protocol () const noexcept + { + return _socket.protocol (); + } + + /** + * @brief get the maximum transmission unit. + * @return MTU. + */ + int mtu () const + { + return _socket.mtu (); + } + + /** + * @brief returns the Time-To-Live value. + * @return The Time-To-Live value. + */ + int ttl () const noexcept + { + return _socket.ttl (); + } + + /** + * @brief get the local endpoint. + * @return local endpoint. + */ + Endpoint localEndpoint () const noexcept + { + return _socket.localEndpoint (); + } + + /** + * @brief get the remote endpoint. + * @return remote endpoint. + */ + Endpoint remoteEndpoint () const noexcept + { + return _socket.remoteEndpoint (); + } + + private: + /** + * @brief handle TLS error + * @param error result returned by a previous call of the openssl API. + * @return -1 is always returned. + */ + int handleTlsError (int result) noexcept + { + switch (SSL_get_error (_ssl.get (), result)) + { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_X509_LOOKUP: + // want read, want write or want lookup. + lastError = make_error_code (Errc::TemporaryError); + break; + + case SSL_ERROR_ZERO_RETURN: + // a close notify alert was received. + // we have to answer by sending a close notify alert too. + lastError = make_error_code (TlsErrc::TlsCloseNotifyAlert); + break; + + case SSL_ERROR_SYSCALL: + // an error occurred at the socket level. + if (errno == 0 || errno == ECONNRESET || errno == EPIPE) + { + lastError = make_error_code (Errc::ConnectionClosed); + } + else + { + lastError = std::error_code (errno, std::generic_category ()); + } + break; + + default: + // SSL protocol error. +#ifdef DEBUG + std::cout << ERR_reason_error_string (ERR_get_error ()) << std::endl; +#endif + lastError = make_error_code (TlsErrc::TlsProtocolError); + break; + } + + return -1; + } + + /** + * @brief c style info callback wrapper. + * @param ssl SSL object. + * @param where context flags. + * @param ret error condition. + */ + static void infoWrapper (const SSL* ssl, int where, int ret) noexcept + { + assert (ssl); + static_cast*> (SSL_get_app_data (ssl))->infoCallback (where, ret); + } + + /** + * @brief SSL state info callback. + * @param where context flags. + * @param ret error condition. + */ + void infoCallback (int where, int ret) const noexcept + { + if (where & SSL_CB_ALERT) + { + std::cout << "SSL/TLS Alert "; + (where & SSL_CB_READ) ? std::cout << "[read] " : std::cout << "[write] "; + std::cout << SSL_alert_type_string_long (ret) << ":"; + std::cout << SSL_alert_desc_string_long (ret); + std::cout << std::endl; + } + else if (where & SSL_CB_LOOP) + { + std::cout << "SSL/TLS State "; + (SSL_in_connect_init (_ssl.get ())) ? std::cout << "[connect] " + : (SSL_in_accept_init (_ssl.get ())) ? std::cout << "[accept] " + : std::cout << "[undefined] "; + std::cout << SSL_state_string_long (_ssl.get ()); + std::cout << std::endl; + } + else if (where & SSL_CB_HANDSHAKE_START) + { + std::cout << "SSL/TLS Handshake [Start] " << SSL_state_string_long (_ssl.get ()) << std::endl; + } + else if (where & SSL_CB_HANDSHAKE_DONE) + { + std::cout << "SSL/TLS Handshake [Done] " << SSL_state_string_long (_ssl.get ()) << std::endl; + std::cout << SSL_CTX_sess_number (_ctx.handle ()) << " items in the session cache" << std::endl; + std::cout << SSL_CTX_sess_connect (_ctx.handle ()) << " client connects" << std::endl; + std::cout << SSL_CTX_sess_connect_good (_ctx.handle ()) << " client connects that finished" + << std::endl; + std::cout << SSL_CTX_sess_connect_renegotiate (_ctx.handle ()) << " client renegotiations requested" + << std::endl; + std::cout << SSL_CTX_sess_accept (_ctx.handle ()) << " server connects" << std::endl; + std::cout << SSL_CTX_sess_accept_good (_ctx.handle ()) << " server connects that finished" << std::endl; + std::cout << SSL_CTX_sess_accept_renegotiate (_ctx.handle ()) << " server renegotiations requested" + << std::endl; + std::cout << SSL_CTX_sess_hits (_ctx.handle ()) << " session cache hits" << std::endl; + std::cout << SSL_CTX_sess_cb_hits (_ctx.handle ()) << " external session cache hits" << std::endl; + std::cout << SSL_CTX_sess_misses (_ctx.handle ()) << " session cache misses" << std::endl; + std::cout << SSL_CTX_sess_timeouts (_ctx.handle ()) << " session cache timeouts" << std::endl; + std::cout << "negotiated " << SSL_get_cipher (_ssl.get ()) << " cipher suite" << std::endl; + } + } + + /** + * @brief c style verify callback wrapper. + * @param preverified pre-verification status. + * @param x509Ctx X509 store context. + * @return 1 if verified, 0 otherwise. + */ + static int verifyWrapper (int preverified, X509_STORE_CTX* x509Ctx) noexcept + { + SSL* ssl = static_cast (X509_STORE_CTX_get_ex_data (x509Ctx, SSL_get_ex_data_X509_STORE_CTX_idx ())); + assert (ssl); + return static_cast*> (SSL_get_app_data (ssl))->verifyCallback (preverified, x509Ctx); + } + + /** + * @brief verify peer certificate. + * @param preverified pre-verification status. + * @param x509Ctx X509 store context. + * @return 1 if verified, 0 otherwise. + */ + int verifyCallback (int preverified, X509_STORE_CTX* context) noexcept + { + int maxDepth = SSL_get_verify_depth (_ssl.get ()); + int dpth = X509_STORE_CTX_get_error_depth (context); + +#ifdef DEBUG + std::cout << "verification started at depth=" << dpth << std::endl; +#endif + + // catch a too long certificate chain. + if ((maxDepth >= 0) && (dpth > maxDepth)) + { + preverified = 0; + X509_STORE_CTX_set_error (context, X509_V_ERR_CERT_CHAIN_TOO_LONG); + } + + if (!preverified) + { +#ifdef DEBUG + std::cout << "verification failed at depth=" << dpth << " - " + << X509_verify_cert_error_string (X509_STORE_CTX_get_error (context)) << std::endl; +#endif + return 0; + } + + // check the certificate host name. + if (!verifyCert (context)) + { +#ifdef DEBUG + std::cout << "rejected by CERT at depth=" << dpth << std::endl; +#endif + return 0; + } + + // check the revocation list. + /*if (!verifyCrl (context)) + { + #ifdef DEBUG + std::cout << "rejected by CRL at depth=" << dpth << std::endl; + #endif + return 0; + }*/ + + // check ocsp. + /*if (!verifyOcsp (context)) + { + #ifdef DEBUG + std::cout << "rejected by OCSP at depth=" << dpth << std::endl; + #endif + return 0; + }*/ + +#ifdef DEBUG + std::cout << "certificate accepted at depth=" << dpth << std::endl; +#endif + + return 1; + } + + /** + * @brief verify certificate validity. + * @param context pointer to the complete context used for the certificate chain verification. + * @return when verified successfully, the callback should return 1, 0 otherwise. + */ + int verifyCert (X509_STORE_CTX* context) const + { + int depth = X509_STORE_CTX_get_error_depth (context); + X509* cert = X509_STORE_CTX_get_current_cert (context); + + char buf[256]; + X509_NAME_oneline (X509_get_subject_name (cert), buf, sizeof (buf)); +#ifdef DEBUG + std::cout << "subject=" << buf << std::endl; +#endif + + // check the certificate host name + if (depth == 0) + { + // confirm a match between the hostname and the hostnames listed in the certificate. + if (!checkHostname (cert)) + { +#ifdef DEBUG + std::cout << "no match for hostname in the certificate" << std::endl; +#endif + return 0; + } + } + + return 1; + } + + /** + * @brief check certificate hostname against remote endpoint. + * @param certificate X509 certificate. + * @return true if hostname matches. + */ + bool checkHostname (X509* certificate) const noexcept + { + bool match = false; + + // get remote hostname name. + std::string serverName (_socket.remoteEndpoint ().hostname ()); + + // strip off trailing dots. + if (!serverName.empty () && serverName.back () == '.') + { + serverName.pop_back (); + } + + // get alternative names. + join::StackOfGeneralNamePtr altnames (reinterpret_cast ( + X509_get_ext_d2i (certificate, NID_subject_alt_name, 0, 0))); + if (altnames) + { + for (int i = 0; (i < sk_GENERAL_NAME_num (altnames.get ())) && !match; ++i) + { + // get a handle to alternative name. + GENERAL_NAME* current_name = sk_GENERAL_NAME_value (altnames.get (), i); + + if (current_name->type == GEN_DNS) + { + // get data and length. + const char* host = reinterpret_cast (ASN1_STRING_get0_data (current_name->d.ia5)); + size_t len = size_t (ASN1_STRING_length (current_name->d.ia5)); + std::string pattern (host, host + len); + + // strip off trailing dots. + if (!pattern.empty () && pattern.back () == '.') + { + pattern.pop_back (); + } + + // compare to pattern. + if (fnmatch (pattern.c_str (), serverName.c_str (), 0) == 0) + { + // an alternative name matched the server hostname. + match = true; + } + } + } + } + + return match; + } + + /** + * @brief verify certificate revocation using CRL. + * @param context pointer to the complete context used for the certificate chain verification. + * @return when verified successfully, the callback should return 1, 0 otherwise. + */ + /*int verifyCrl ([[maybe_unused]]X509_STORE_CTX *context) const + { + return 1; + }*/ + + /** + * @brief verify certificate revocation using OCSP. + * @param context pointer to the complete context used for the certificate chain verification. + * @return when verified successfully, the callback should return 1, 0 otherwise. + */ + /*int verifyOcsp ([[maybe_unused]]X509_STORE_CTX *context) const + { + return 1; + }*/ + + /// underlying socket. + Socket _socket; + + /// TLS context. + TlsContext _ctx; + + /// TLS handle. + SslPtr _ssl; + }; + + /** + * @brief compare two TLS streams based on their underlying socket handle. + * @param a first TLS stream. + * @param b second TLS stream. + * @return true if the handle of a is less than the handle of b, false otherwise. + */ + template + constexpr bool operator< (const TlsWrapper& a, const TlsWrapper& b) noexcept + { + return a.handle () < b.handle (); + } +} + +#endif diff --git a/crypto/src/tlscontext.cpp b/crypto/src/tlscontext.cpp new file mode 100644 index 00000000..88b86228 --- /dev/null +++ b/crypto/src/tlscontext.cpp @@ -0,0 +1,367 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include +#include + +// C. +#include + +using join::TlsContext; + +// ========================================================================= +// CLASS : TlsContext +// METHOD : TlsContext +// ========================================================================= +TlsContext::TlsContext (Role role) +: _role (role) +, _ctx (SSL_CTX_new (method (role)), SSL_CTX_free) +{ + if (!_ctx) + { + throw std::runtime_error ("SSL_CTX_new failed"); // LCOV_EXCL_LINE + } + + // enable the OpenSSL bug workaround options. + SSL_CTX_set_options (_ctx.get (), SSL_OP_ALL); + + // disallow compression. + SSL_CTX_set_options (_ctx.get (), SSL_OP_NO_COMPRESSION); + + // disallow usage of SSLv2, SSLv3, TLSv1 and TLSv1.1 which are insecure. + SSL_CTX_set_options (_ctx.get (), SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); + + // setup write mode. + SSL_CTX_set_mode (_ctx.get (), SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + // automatically renegotiates. + SSL_CTX_set_mode (_ctx.get (), SSL_MODE_AUTO_RETRY); + + // no verification by default. + SSL_CTX_set_verify (_ctx.get (), SSL_VERIFY_NONE, nullptr); + + // set default TLSv1.2 and below cipher suites. + SSL_CTX_set_cipher_list (_ctx.get (), defaultCipher.c_str ()); + + // set default TLSv1.3 cipher suites. + SSL_CTX_set_ciphersuites (_ctx.get (), defaultCipher_1_3.c_str ()); + + if (isServer ()) + { + // choose the cipher according to the server's preferences. + SSL_CTX_set_options (_ctx.get (), SSL_OP_CIPHER_SERVER_PREFERENCE); + + int sid = randomize (); + + // enable session caching. + SSL_CTX_set_session_id_context (_ctx.get (), reinterpret_cast (&sid), sizeof (sid)); + + // disallow client-side renegotiation. + SSL_CTX_set_options (_ctx.get (), SSL_OP_NO_RENEGOTIATION); + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + // use the default built-in Diffie-Hellman parameters. + SSL_CTX_set_dh_auto (_ctx.get (), 1); + + // Set elliptic curve Diffie-Hellman key. + SSL_CTX_set1_groups_list (_ctx.get (), defaultCurve.c_str ()); +#else + // Set Diffie-Hellman key. + DhKeyPtr dh (getDh2236 ()); + if (dh) + { + SSL_CTX_set_tmp_dh (_ctx.get (), dh.get ()); + } + + // Set elliptic curve Diffie-Hellman key. + EcdhKeyPtr ecdh (EC_KEY_new_by_curve_name (NID_X9_62_prime256v1)); + if (ecdh) + { + SSL_CTX_set_tmp_ecdh (_ctx.get (), ecdh.get ()); + } +#endif + + // set session cache mode. + SSL_CTX_set_session_cache_mode (_ctx.get (), SSL_SESS_CACHE_SERVER); + } + else + { + // set session cache mode. + SSL_CTX_set_session_cache_mode (_ctx.get (), SSL_SESS_CACHE_CLIENT); + } +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : setCertificate +// ========================================================================= +int TlsContext::setCertificate (const std::string& cert, const std::string& key) noexcept +{ + if (SSL_CTX_use_certificate_file (_ctx.get (), cert.c_str (), SSL_FILETYPE_PEM) == 0) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + if (key.size ()) + { + if (SSL_CTX_use_PrivateKey_file (_ctx.get (), key.c_str (), SSL_FILETYPE_PEM) == 0) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + } + + if (SSL_CTX_check_private_key (_ctx.get ()) == 0) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + return 0; +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : setCaPath +// ========================================================================= +int TlsContext::setCaPath (const std::string& caPath) +{ + struct stat st; + if (stat (caPath.c_str (), &st) != 0 || !S_ISDIR (st.st_mode) || + SSL_CTX_load_verify_locations (_ctx.get (), nullptr, caPath.c_str ()) == 0) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + return 0; +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : setCaFile +// ========================================================================= +int TlsContext::setCaFile (const std::string& caFile) +{ + struct stat st; + if (stat (caFile.c_str (), &st) != 0 || !S_ISREG (st.st_mode) || + SSL_CTX_load_verify_locations (_ctx.get (), caFile.c_str (), nullptr) == 0) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + return 0; +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : setVerify +// ========================================================================= +void TlsContext::setVerify (bool verify, int depth) noexcept +{ + _verify = verify; + _depth = depth; +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : setCipher +// ========================================================================= +int TlsContext::setCipher (const std::string& cipher) noexcept +{ + if (SSL_CTX_set_cipher_list (_ctx.get (), cipher.c_str ()) == 0) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + return 0; +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : setCipher_1_3 +// ========================================================================= +int TlsContext::setCipher_1_3 (const std::string& cipher) noexcept +{ + if (SSL_CTX_set_ciphersuites (_ctx.get (), cipher.c_str ()) == 0) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + return 0; +} + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +// ========================================================================= +// CLASS : TlsContext +// METHOD : setCurve +// ========================================================================= +int TlsContext::setCurve (const std::string& curves) +{ + if (SSL_CTX_set1_groups_list (_ctx.get (), curves.c_str ()) == 0) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + return 0; +} +#endif + +// ========================================================================= +// CLASS : TlsContext +// METHOD : setAlpnProtocols +// ========================================================================= +int TlsContext::setAlpnProtocols (const std::vector& protocols) +{ + std::vector wire; + wire.reserve (256); + + for (const auto& proto : protocols) + { + if (proto.size () > 255) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + wire.push_back (static_cast (proto.size ())); + wire.insert (wire.end (), proto.begin (), proto.end ()); + } + + if (SSL_CTX_set_alpn_protos (_ctx.get (), wire.data (), static_cast (wire.size ())) != 0) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + return 0; +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : handle +// ========================================================================= +SSL_CTX* TlsContext::handle () const noexcept +{ + return _ctx.get (); +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : verify +// ========================================================================= +bool TlsContext::verify () const noexcept +{ + return _verify; +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : depth +// ========================================================================= +int TlsContext::depth () const noexcept +{ + return _depth; +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : isServer +// ========================================================================= +bool TlsContext::isServer () const noexcept +{ + return (_role == Role::TlsServer) || (_role == Role::DtlsServer); +} + +// ========================================================================= +// CLASS : TlsContext +// METHOD : method +// ========================================================================= +const SSL_METHOD* TlsContext::method (Role role) noexcept +{ + switch (role) + { + case Role::TlsServer: + return TLS_server_method (); + case Role::DtlsClient: + return DTLS_client_method (); + case Role::DtlsServer: + return DTLS_server_method (); + default: + return TLS_client_method (); + } +} + +#if OPENSSL_VERSION_NUMBER < 0x30000000L +// ========================================================================= +// CLASS : TlsContext +// METHOD : getDh2236 +// ========================================================================= +DH* TlsContext::getDh2236 () +{ + static unsigned char dhp_2236[] = { + 0x0C, 0xA5, 0x51, 0x2B, 0x8F, 0xF7, 0xA8, 0x74, 0x4D, 0x52, 0xD7, 0xED, 0x97, 0x83, 0xA4, 0xD2, 0x8B, 0xF3, + 0xE7, 0x92, 0xF0, 0x27, 0x1B, 0xA0, 0x80, 0x83, 0x19, 0xDD, 0x02, 0xEF, 0xA3, 0xE6, 0x13, 0x0A, 0x47, 0xE6, + 0xF1, 0x3B, 0xC1, 0x5F, 0x63, 0xC4, 0x03, 0xBA, 0xAC, 0xAA, 0xA3, 0x44, 0xC2, 0x03, 0x6D, 0x62, 0x33, 0xAA, + 0xF9, 0xA2, 0x5A, 0x98, 0xC2, 0xC0, 0x71, 0x6F, 0xB0, 0x93, 0x6A, 0x26, 0x92, 0x90, 0x95, 0xEA, 0xE8, 0x5F, + 0x81, 0x50, 0x57, 0xB3, 0xB7, 0xE6, 0x3A, 0x3A, 0x90, 0x15, 0x01, 0x2F, 0xC7, 0x8F, 0xAA, 0x0C, 0xAE, 0xC0, + 0xFF, 0x3A, 0xA7, 0x26, 0x5C, 0x87, 0xC2, 0x00, 0x68, 0xCA, 0x02, 0x06, 0x50, 0x44, 0xEE, 0x75, 0xE7, 0xFF, + 0x16, 0xD1, 0x0F, 0x64, 0x51, 0x97, 0x52, 0x54, 0x69, 0xF0, 0x31, 0x81, 0x4D, 0xEB, 0xF5, 0xA8, 0xB3, 0x7B, + 0x48, 0x60, 0xBD, 0xC7, 0xC9, 0x6E, 0x97, 0x86, 0x9B, 0xE6, 0x66, 0x4E, 0x1D, 0xE5, 0x6F, 0xBA, 0xC5, 0x3D, + 0xFD, 0x3F, 0x34, 0x69, 0x6F, 0xC0, 0xFA, 0x8D, 0x42, 0x73, 0xA2, 0x49, 0xDE, 0xB6, 0x8D, 0x71, 0x15, 0xFC, + 0xB4, 0x18, 0x31, 0x5A, 0x24, 0xD0, 0x5E, 0xA8, 0xE0, 0xD8, 0x1C, 0xF8, 0x0F, 0x1F, 0x59, 0x22, 0x5A, 0x07, + 0x75, 0x06, 0x98, 0x58, 0xE1, 0xF6, 0xA5, 0x53, 0xFD, 0x66, 0x1E, 0x8F, 0x41, 0x63, 0x61, 0xA1, 0x79, 0x0D, + 0x3B, 0xA7, 0xF4, 0xBD, 0x72, 0xEB, 0xE1, 0xDC, 0xE2, 0xC9, 0x9B, 0x41, 0xF6, 0x33, 0x3F, 0x9F, 0x0C, 0x33, + 0x7B, 0xF2, 0x90, 0x68, 0x28, 0xD3, 0x5A, 0xC1, 0x5C, 0xDE, 0x15, 0x11, 0xF4, 0xDD, 0xCB, 0x09, 0x78, 0x63, + 0x3B, 0xB6, 0xE8, 0xEE, 0x9A, 0x48, 0xE9, 0x79, 0x80, 0x3F, 0x34, 0x8D, 0xB9, 0x24, 0x8D, 0x94, 0x88, 0xA9, + 0x75, 0xA5, 0x19, 0x05, 0x8D, 0x77, 0x20, 0xAF, 0xC2, 0xC9, 0x7B, 0xD2, 0x51, 0xEE, 0x17, 0x22, 0xAC, 0x33, + 0xA8, 0xA6, 0x1B, 0x8B, 0xE3, 0x79, 0xF3, 0xE8, 0x3B, 0x6B}; + static unsigned char dhg_2236[] = {0x02}; + + DH* dh = DH_new (); + if (dh == nullptr) + { + return nullptr; + } + + BIGNUM* p = BN_bin2bn (dhp_2236, sizeof (dhp_2236), nullptr); + BIGNUM* g = BN_bin2bn (dhg_2236, sizeof (dhg_2236), nullptr); + + if ((p == nullptr) || (g == nullptr) || !DH_set0_pqg (dh, p, nullptr, g)) + { + DH_free (dh); + BN_free (p); + BN_free (g); + return nullptr; + } + + return dh; +} +#endif diff --git a/crypto/src/tlserror.cpp b/crypto/src/tlserror.cpp new file mode 100644 index 00000000..13f4edad --- /dev/null +++ b/crypto/src/tlserror.cpp @@ -0,0 +1,83 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include + +using join::TlsCategory; +using join::TlsErrc; + +// ========================================================================= +// CLASS : TlsCategory +// METHOD : name +// ========================================================================= +const char* TlsCategory::name () const noexcept +{ + return "libjoin"; +} + +// ========================================================================= +// CLASS : TlsCategory +// METHOD : message +// ========================================================================= +std::string TlsCategory::message (int code) const +{ + switch (static_cast (code)) + { + case TlsErrc::TlsCloseNotifyAlert: + return "TLS close notify alert received"; + case TlsErrc::TlsProtocolError: + return "TLS protocol error"; + default: + return "success"; + } +} + +// ========================================================================= +// CLASS : +// METHOD : getTlsCategory +// ========================================================================= +const std::error_category& join::getTlsCategory () +{ + static TlsCategory instance; + return instance; +} + +// ========================================================================= +// CLASS : +// METHOD : make_error_code +// ========================================================================= +std::error_code join::make_error_code (TlsErrc code) +{ + return std::error_code (static_cast (code), getTlsCategory ()); +} + +// ========================================================================= +// CLASS : +// METHOD : make_error_condition +// ========================================================================= +std::error_condition join::make_error_condition (TlsErrc code) +{ + return std::error_condition (static_cast (code), getTlsCategory ()); +} diff --git a/crypto/tests/CMakeLists.txt b/crypto/tests/CMakeLists.txt index a496b843..28de04a5 100644 --- a/crypto/tests/CMakeLists.txt +++ b/crypto/tests/CMakeLists.txt @@ -31,3 +31,23 @@ add_executable(signature.gtest signature_test.cpp) target_link_libraries(signature.gtest ${JOIN_CRYPTO} GTest::gtest_main) add_test(NAME signature.gtest COMMAND signature.gtest) install(TARGETS signature.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +# add_executable(tlserror.gtest tlserror_test.cpp) +# target_link_libraries(tlserror.gtest ${JOIN_CRYPTO} GTest::gtest_main) +# add_test(NAME tlserror.gtest COMMAND tlserror.gtest) +# install(TARGETS tlserror.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(tlscontext.gtest tlscontext_test.cpp) +target_link_libraries(tlscontext.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME tlscontext.gtest COMMAND tlscontext.gtest) +install(TARGETS tlscontext.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(tlswrapper.gtest tlswrapper_test.cpp) +target_link_libraries(tlswrapper.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME tlswrapper.gtest COMMAND tlswrapper.gtest) +install(TARGETS tlswrapper.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(dtlswrapper.gtest dtlswrapper_test.cpp) +target_link_libraries(dtlswrapper.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME dtlswrapper.gtest COMMAND dtlswrapper.gtest) +install(TARGETS dtlswrapper.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) diff --git a/crypto/tests/dtlswrapper_test.cpp b/crypto/tests/dtlswrapper_test.cpp new file mode 100644 index 00000000..c8097644 --- /dev/null +++ b/crypto/tests/dtlswrapper_test.cpp @@ -0,0 +1,1032 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include +#include +#include + +// Libraries. +#include + +// C++. +#include + +using join::Errc; +using join::IpAddress; +using join::ReactorThread; +using join::EventHandler; +using join::Udp; +using join::TlsContext; +using join::TlsWrapper; + +/** + * @brief Class used to test the datagram TLS wrapper API. + */ +class DtlsSocket : public EventHandler, public ::testing::Test +{ +public: + /** + * @brief set up test case. + */ + static void SetUpTestCase () + { + std::ofstream rootCertFile (_rootcert); + if (rootCertFile.is_open ()) + { + rootCertFile << "-----BEGIN CERTIFICATE-----" << std::endl; + rootCertFile << "MIIChjCCAisCFBuHxbqMUGyl7OQUQcoRg3pOBJF+MAoGCCqGSM49BAMCMIHEMQsw" << std::endl; + rootCertFile << "CQYDVQQGEwJGUjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVz" << std::endl; + rootCertFile << "MRcwFQYDVQQKDA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdv" << std::endl; + rootCertFile << "cmsgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3" << std::endl; + rootCertFile << "b3JrLm5ldDEoMCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5l" << std::endl; + rootCertFile << "dDAeFw0yMjA3MDUxNjMxMTZaFw0zMjA3MDIxNjMxMTZaMIHEMQswCQYDVQQGEwJG" << std::endl; + rootCertFile << "UjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVzMRcwFQYDVQQK" << std::endl; + rootCertFile << "DA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdvcmsgQ2VydGlm" << std::endl; + rootCertFile << "aWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3b3JrLm5ldDEo" << std::endl; + rootCertFile << "MCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5ldDBZMBMGByqG" << std::endl; + rootCertFile << "SM49AgEGCCqGSM49AwEHA0IABASk0zCrKtXQi0Ycx+Anx+VWv8gncbPmNQ1yutii" << std::endl; + rootCertFile << "gQjP2mF9NIqlxpcKNuE/6DDnfSzCEDhFyvGiK0NJ1C3RBowwCgYIKoZIzj0EAwID" << std::endl; + rootCertFile << "SQAwRgIhAIFqdbxTb5kRjy4UY0N205ZEhHSMK89p2oUyn4iNbXH2AiEAtmV1UyRX" << std::endl; + rootCertFile << "DIAGr/F+1SwQMPoJzSQxZ7NdxjNgW286e9Q=" << std::endl; + rootCertFile << "-----END CERTIFICATE-----" << std::endl; + rootCertFile.close (); + } + + mkdir (_certPath.c_str (), 0777); + std::ofstream certFile (_certFile); + if (certFile.is_open ()) + { + certFile << "-----BEGIN CERTIFICATE-----" << std::endl; + certFile << "MIIDgDCCAyagAwIBAgIUR3ZIuKMt0BdaOZQnPwhSMR9qzfgwCgYIKoZIzj0EAwIw" << std::endl; + certFile << "gcQxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nh" << std::endl; + certFile << "c3RyZXMxFzAVBgNVBAoMDkpvaW4gRnJhbWV3b3JrMS0wKwYDVQQLDCRKb2luIEZy" << std::endl; + certFile << "YW1ld29yayBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHTAbBgNVBAMMFGNhLmpvaW5m" << std::endl; + certFile << "cmFtZXdvcmsubmV0MSgwJgYJKoZIhvcNAQkBFhlzdXBwb3J0QGpvaW5mcmFtZXdv" << std::endl; + certFile << "cmsubmV0MB4XDTIyMDcwNzEyMTIxMFoXDTMyMDcwNDEyMTIxMFowgagxCzAJBgNV" << std::endl; + certFile << "BAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nhc3RyZXMxFzAV" << std::endl; + certFile << "BgNVBAoMDkpvaW4gRnJhbWV3b3JrMRswGQYDVQQLDBJKb2luIEZyYW1ld29yayBE" << std::endl; + certFile << "ZXYxEzARBgNVBAMMCmxvY2FsaG9zdC4xKDAmBgkqhkiG9w0BCQEWGXN1cHBvcnRA" << std::endl; + certFile << "am9pbmZyYW1ld29yay5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" << std::endl; + certFile << "AQDSNtw5zEoJFPf6Rl0Y1n8BQfE0YTPCELvFAeioUfj8CAnUleHL9pwAEFg6kgoG" << std::endl; + certFile << "hvwto5/yWGPUqNNfe3xbFTJcHgMhgtjqy5H6sYDkTi3kYIIMBfTHr8NI7HWE8Nz1" << std::endl; + certFile << "qU1snjtERnkoLilIZf/2BojNVMtHC1H316WbMicXS0v7HQo3lv6PYSana9Q9ow9O" << std::endl; + certFile << "2/FiW5qq1eOhI1ZedRanX+bl0jHWCd3WsI87+5bTaQrfetdHTOmav6O17Iq9FiTh" << std::endl; + certFile << "Sg9fbM3s2Hw15kI+mws029dhcwXs5sYY+NgtrQwjR5qH+54BdUaPwQfl/KyulfEl" << std::endl; + certFile << "TJykJ+3w6MorxUr55F68uBNbAgMBAAGjRTBDMAsGA1UdDwQEAwIF4DAdBgNVHSUE" << std::endl; + certFile << "FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwFQYDVR0RBA4wDIIKbG9jYWxob3N0LjAK" << std::endl; + certFile << "BggqhkjOPQQDAgNIADBFAiA120ufIbhcw7BJQ1L6WudDdW2mHrVXvdgeOzVGgz1d" << std::endl; + certFile << "iAIhAMm/sWI3yzb2IMPffxWKYusWEQE2hZvs24ESSC/ZZ0s+" << std::endl; + certFile << "-----END CERTIFICATE-----" << std::endl; + certFile.close (); + } + + [[maybe_unused]] int result; + result = std::system (std::string ("/usr/bin/c_rehash " + _certPath).c_str ()); + + std::ofstream keyFile (_key); + if (keyFile.is_open ()) + { + keyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; + keyFile << "MIIEowIBAAKCAQEA0jbcOcxKCRT3+kZdGNZ/AUHxNGEzwhC7xQHoqFH4/AgJ1JXh" << std::endl; + keyFile << "y/acABBYOpIKBob8LaOf8lhj1KjTX3t8WxUyXB4DIYLY6suR+rGA5E4t5GCCDAX0" << std::endl; + keyFile << "x6/DSOx1hPDc9alNbJ47REZ5KC4pSGX/9gaIzVTLRwtR99elmzInF0tL+x0KN5b+" << std::endl; + keyFile << "j2Emp2vUPaMPTtvxYluaqtXjoSNWXnUWp1/m5dIx1gnd1rCPO/uW02kK33rXR0zp" << std::endl; + keyFile << "mr+jteyKvRYk4UoPX2zN7Nh8NeZCPpsLNNvXYXMF7ObGGPjYLa0MI0eah/ueAXVG" << std::endl; + keyFile << "j8EH5fysrpXxJUycpCft8OjKK8VK+eRevLgTWwIDAQABAoIBAAzdlK7o5OMXaHHl" << std::endl; + keyFile << "2o7Jme5Oxd9pz4wiEAvnqQCcO7vZFhjvr2kXR8btOSkkhP6PRmHYsNJZPIroZj9i" << std::endl; + keyFile << "xGKisnlW0OQ9KN995ApO0M+oRUDD81GfD7Mk+7O73Rls0GksmnN6X7A3C/U8lgQ7" << std::endl; + keyFile << "UeYR0k+Wz/YiKDsd9KHB+QiA8D6HFQ9I8Y2P97KOcYnxXZfSwNm+ENNU3wShZOl2" << std::endl; + keyFile << "ZYJJ4DE+5m2SwZ6g8b5Zre4cDbOduwuz/jXzjy2tAZBlTS4DVpYlhd14z+ssUWiu" << std::endl; + keyFile << "AdS/nqSF7Obj0TRhoGNfrkisFzV4itavQ5DKGj/6hjueIJVLteUOzcCeg26YosNy" << std::endl; + keyFile << "QzZSjOECgYEA7y3InEoh93/4HZCZmdwN8KfZtqirX0t966FntgAT8RkIs+KvNS8B" << std::endl; + keyFile << "m3RfNLa/EuDt5zTmHRGx+oeN+17i9QQjKWcR0NnJ6aSZbvJByj3yKxLF9XVllzp/" << std::endl; + keyFile << "vHSSyB264RoKIrWmFN6cCO4u4h9ZPY75pASWBCDMdnGK8axAcqAnlqsCgYEA4P+Y" << std::endl; + keyFile << "FF9RW4rhrVU4dpXSfcr6vOwqfp9F9vhTVL0JS/SLOFoJNNpS9Rnq3pVLEuKyCphd" << std::endl; + keyFile << "3nk9VFfoRygmMaGBvwGaXZPPvosoaIUgOdTt7KIfSHPichBEVxRuWCrtTGGkG0ok" << std::endl; + keyFile << "s/RPHhvxZE267vsVj1PktK8Yr5Ba0AL2ycztNhECgYB5OAwHYe8LIBlg6otelk+e" << std::endl; + keyFile << "W4OU9rE8L+eWx4vniuyQce6eNNI1syguYHFsJv56E/OfDYlezDwWzCLidnmyUjF7" << std::endl; + keyFile << "51f5MJgLyTdWKoO7e1/EAtS/jYs6dRSOL8rAj4jKU0c1xjhxNU2BnS23vsmc0Fyn" << std::endl; + keyFile << "iwd4+iKGGQ+hYnqbXZ4S1wKBgD/3an0gPDkSWua0e8D7B0TMGEztt4cYMQPtxYMp" << std::endl; + keyFile << "2yLE+2+h6UwlZcBZBfUR7K4J1SQ9/THqtgzskRTpzTH/AKwVAJXqF/3MAkj00Byg" << std::endl; + keyFile << "9KN50/r9NzvGdCdtn5FhYuV8PPOlOJoQsw2UVCR4FNUsfQyqhTL5NMN0/tx0e0UU" << std::endl; + keyFile << "BbyBAoGBANu5ifByauVELH8UEl5rXRu1S9iAVV+Bc5jboXwc4VxJtEyomGJ7+YdL" << std::endl; + keyFile << "5c9LFV+STUp7CE12uSXQZTQM0tEjPinLntRinNzu9tIHR1vy7FZHEwMFIgB4VTY7" << std::endl; + keyFile << "ALRYv1/QpTuywpNUFRS15JkfGNf5JIkrUEWLgkX3OVCBsRGHUugy" << std::endl; + keyFile << "-----END RSA PRIVATE KEY-----" << std::endl; + keyFile.close (); + } + + std::ofstream invalidKeyFile (_invalidKey); + if (invalidKeyFile.is_open ()) + { + invalidKeyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; + invalidKeyFile << "MIIEowIBAAKCAQEA2Q0DOyG039uVMuxNnZ5fpfOcvXXOTguST1QR6eLVkdG7OKpM" << std::endl; + invalidKeyFile << "nc9K597jx1syT1q+SwFcykMtvWxCfD8BR7bcLILeO6z+HlRfvjOhUiHaX/KCaTN8" << std::endl; + invalidKeyFile << "l7OJOgmUlL0FhQ1SXxw7KCSGd+rgu1iHwjFDDkj/tG24ashdmNt+DYdeoJu2mzgw" << std::endl; + invalidKeyFile << "tEASfG9VjqBR7ni4Hg/sRpwXvEK5nI1JSLyZbcPCxGlBRdB8hMdny/VW+SBwKD2/" << std::endl; + invalidKeyFile << "ivpVJLulw2oniSIcCCcr9d+ERY4XrO71UsiACwPxfdEtbG0KrZfpK91k7vl64DHM" << std::endl; + invalidKeyFile << "CeTQPKRZm+LDKOUfv/eTF9F6GY4Dpw2LMwLM5QIDAQABAoIBABjV91etzK+Mxa61" << std::endl; + invalidKeyFile << "AVCWzaUEkhvPvhKKGmy/VulnTj7IO98JBYlNLeoIRBIMql4QKRQWDNMMCtDQ8W6c" << std::endl; + invalidKeyFile << "Gv5kux7QvrMfYViBGQ9/gucN/pnZ+vgkrw4AuiQM8pZuZpJJ6vH9HfvC6iwQkTR+" << std::endl; + invalidKeyFile << "tdIPpvecfL3djCuTz7ns66iKo9ZGpRE6emTBynr8og/oqD8Vw5bW+JJ+AJ3IqZf4" << std::endl; + invalidKeyFile << "NslNist7d5FZ5N/+nxWyBUcFglP7bZzb/raOVc/flrYIeDy72asnWOYbDTPzMyH1" << std::endl; + invalidKeyFile << "dfaox6QKZtA5NdO9x4aHHGgAz8BTgqs7LvxPwoH+XF1dDCsb3kIeQxHTfcc1opMw" << std::endl; + invalidKeyFile << "atxpgwECgYEA8Zq/7Z3tKcBlMz4XNKWWvaDxhBUIS62tGeLJ2spLRFvkL1ixnjcK" << std::endl; + invalidKeyFile << "72YWOwDpoINEWa8AhAhM6afE9VxrupSGg+C9uALaJ8HTWTP6u6/F8sbsYaoWHyA/" << std::endl; + invalidKeyFile << "k/8/nFEr43ciKUjBhMHB42vYidAgiOvDVXc+/k7HIMQfl/vyp32ecEECgYEA5fu9" << std::endl; + invalidKeyFile << "ePLh55TYbXe8SCL0hsZcC8Q/ioT/0GJ6uevGb0lw3XAa+HC6//upu90T7ZOIqysc" << std::endl; + invalidKeyFile << "aAqln7ZEeCfvXI/3YJyJ2RWatD+2itECbd0WV2/JflO/OAzDSSFvpxxmwIzccIeA" << std::endl; + invalidKeyFile << "UNuNcQGD8HDwFzU+sULvF82yuwMt1syPd/mns6UCgYAviqP5vfnNHW7MhotKcMsY" << std::endl; + invalidKeyFile << "xXLA6uKXAbXuQhI2W1g0O2DLcEiDOZGNSilVsvhF/Y6VlzoiwP9hewHmxijsrg1K" << std::endl; + invalidKeyFile << "Jg8vBmCnMhzEkNXl2NC61SnujemMdmwMU03RFKfuOqMePJLX7MiaV75kX/AHAV2O" << std::endl; + invalidKeyFile << "k8hxgk7sw6rz3UACdVWYAQKBgHUu5ScoksS+Cd0VQmF7Nh8qGSKBt2KsS/BxDVmI" << std::endl; + invalidKeyFile << "ck6oHBMomQV340CliaHIjuvh3aRhzhKRQjzz0UVsC8GdNY4LlQ2AvZgUUr2+q78x" << std::endl; + invalidKeyFile << "BL4+nmt43pj/n822dL6wcQaxf2zzDgWlKReojwLHeP5KSgxmL49wZx51CzlEd+HI" << std::endl; + invalidKeyFile << "2pNlAoGBAObdC7woN7jEfdfYz1BhUpmBsIRqW2yLA1DnlK9lfgs2i1w7spzAh2hV" << std::endl; + invalidKeyFile << "djPiKj5vZdcrbaa+SBAnZbFTHyXmAbKbO/iZpSromaZYyCK8NktJu/YxpWZmjnRF" << std::endl; + invalidKeyFile << "2xOadRGCav5fTGzCN/ADLgIo4gIAI2o/UnV/MdaSAdHyIeSrxBAb" << std::endl; + invalidKeyFile << "-----END RSA PRIVATE KEY-----" << std::endl; + invalidKeyFile.close (); + } + } + + /** + * @brief tear down test case. + */ + static void TearDownTestCase () + { + unlink (_rootcert.c_str ()); + unlink (_certFile.c_str ()); + rmdir (_certPath.c_str ()); + unlink (_key.c_str ()); + unlink (_invalidKey.c_str ()); + } + +protected: + /** + * @brief Sets up the test fixture. + */ + void SetUp () override + { + ASSERT_EQ (_tlsContext.setCertificate (_certFile, _key), 0); + ASSERT_EQ (_tlsContext.setCipher (join::defaultCipher), 0); + ASSERT_EQ (_tlsContext.setCipher_1_3 (join::defaultCipher_1_3), 0); + + ASSERT_EQ (_socket.bind ({IpAddress::ipv6Wildcard, _port}), 0) << join::lastError.message (); + ASSERT_EQ (ReactorThread::reactor ().addHandler (_socket.handle (), this), 0); + } + + /** + * @brief Tears down the test fixture. + */ + void TearDown () override + { + ASSERT_EQ (ReactorThread::reactor ().delHandler (_socket.handle ()), 0) << join::lastError.message (); + _socket.close (); + } + + /** + * @brief method called when data are ready to be read on handle. + * @param fd file descriptor. + */ + virtual void onReadable ([[maybe_unused]] int fd) override + { + _socket.waitHandshake (_timeout); + + char buffer[65536]; + Udp::Endpoint from; + int nread = _socket.readFrom (buffer, sizeof (buffer), &from); + if (nread > 0) + { + _socket.writeTo (buffer, nread, from); + } + + _socket.waitShutdown (_timeout); + } + + /// TLS context serveur. + TlsContext _tlsContext{TlsContext::Role::DtlsServer}; + + /// socket. + TlsWrapper _socket{_tlsContext, Udp::Socket::Blocking}; + + /// host. + static const std::string _hostv4; + static const std::string _hostv6; + + /// port. + static const uint16_t _port; + + /// timeout. + static const int _timeout; + + /// root certificate. + static const std::string _rootcert; + + /// certificate path. + static const std::string _certPath; + + /// certificate file. + static const std::string _certFile; + + /// private key. + static const std::string _key; + + /// invalid private key. + static const std::string _invalidKey; +}; + +const std::string DtlsSocket::_hostv4 = "127.0.0.1"; +const std::string DtlsSocket::_hostv6 = "::1"; +const uint16_t DtlsSocket::_port = 5000; +const int DtlsSocket::_timeout = 1000; +const std::string DtlsSocket::_rootcert = "/tmp/tlssocket_test_root.cert"; +const std::string DtlsSocket::_certPath = "/tmp/certs"; +const std::string DtlsSocket::_certFile = _certPath + "/tlssocket_test.cert"; +const std::string DtlsSocket::_key = "/tmp/tlssocket_test.key"; +const std::string DtlsSocket::_invalidKey = "/tmp/tlssocket_test_invalid.key"; + +/** + * @brief Test move. + */ +TEST_F (DtlsSocket, move) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls1 (ctx, Udp::Socket::Blocking), dtls2 (ctx); + + ASSERT_EQ (dtls1.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (dtls1.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_TRUE (dtls1.connected ()); + ASSERT_EQ (dtls1.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls1.encrypted ()); + dtls2 = std::move (dtls1); + ASSERT_TRUE (dtls2.connected ()); + ASSERT_TRUE (dtls2.encrypted ()); + ASSERT_EQ (dtls2.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls2.disconnect (), 0) << join::lastError.message (); + dtls2.close (); +} + +/** + * @brief Test open method. + */ +TEST_F (DtlsSocket, open) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx); + + ASSERT_EQ (dtls.open (Udp::v4 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Udp::v4 ()), -1); + ASSERT_EQ (join::lastError, Errc::InUse); + dtls.close (); + + ASSERT_EQ (dtls.open (Udp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Udp::v6 ()), -1); + ASSERT_EQ (join::lastError, Errc::InUse); + dtls.close (); +} + +/** + * @brief Test close method. + */ +TEST_F (DtlsSocket, close) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_FALSE (dtls.opened ()); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.opened ()); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.opened ()); + dtls.close (); + ASSERT_FALSE (dtls.opened ()); +} + +/** + * @brief Test bind method. + */ +TEST_F (DtlsSocket, bind) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), -1); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); + + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test bindToDevice method. + */ +TEST_F (DtlsSocket, bindToDevice) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_EQ (dtls.bindToDevice ("lo"), -1); + + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.bindToDevice ("lo"), -1); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); + + ASSERT_EQ (dtls.open (Udp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.bindToDevice ("lo"), 0) << join::lastError.message (); + ASSERT_EQ (dtls.connect ({_hostv6, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); + + ASSERT_EQ (dtls.bindToDevice ("foo"), -1); +} + +/** + * @brief Test connect method. + */ +TEST_F (DtlsSocket, connect) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_EQ (dtls.connect ({"255.255.255.255", _port}), -1); + + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), -1); + ASSERT_EQ (join::lastError, Errc::InUse); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test waitConnected method. + */ +// TEST_F (DtlsSocket, waitConnected) +// { +// TlsContext ctx (TlsContext::Role::DtlsClient); +// TlsWrapper dtls (ctx); + +// ASSERT_FALSE (dtls.waitConnected (_timeout)); +// if (dtls.connect ({_hostv4, _port}) == -1) +// { +// ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); +// ASSERT_TRUE (dtls.connecting ()); +// } +// ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); +// if (dtls.disconnect () == -1) +// { +// ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); +// } +// ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); +// dtls.close (); +// } + +/** + * @brief Test handshake method. + */ +TEST_F (DtlsSocket, handshake) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_EQ (dtls.handshake (), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.open (Udp::v4 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test waitHandshake method. + */ +TEST_F (DtlsSocket, waitHandshake) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + + ASSERT_EQ (dtls.open (Udp::v6 ()), 0) << join::lastError.message (); + ASSERT_FALSE (dtls.waitHandshake (_timeout)); + if (dtls.connect ({_hostv6, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); + if (dtls.handshake () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitHandshake (_timeout)) << join::lastError.message (); + ASSERT_TRUE (dtls.waitHandshake (_timeout)) << join::lastError.message (); + if (dtls.shutdown () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitShutdown (_timeout)) << join::lastError.message (); + if (dtls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test disconnect method. + */ +TEST_F (DtlsSocket, disconnect) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_FALSE (dtls.connected ()); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.connected ()); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + ASSERT_FALSE (dtls.connected ()); + dtls.close (); + ASSERT_FALSE (dtls.connected ()); +} + +/** + * @brief Test waitDisconnected method. + */ +TEST_F (DtlsSocket, waitDisconnected) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + + ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); + if (dtls.connect ({_hostv4, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); + ASSERT_FALSE (dtls.waitDisconnected (_timeout)); + if (dtls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test waitReadyRead method. + */ +TEST_F (DtlsSocket, waitReadyRead) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_FALSE (dtls.waitReadyRead (_timeout)); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + if (dtls.connect ({_hostv4, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); + if (dtls.handshake () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitHandshake (_timeout)) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyRead (_timeout)) << join::lastError.message (); + if (dtls.shutdown () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitShutdown (_timeout)) << join::lastError.message (); + if (dtls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test read method. + */ +TEST_F (DtlsSocket, read) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_EQ (dtls.read (data, sizeof (data)), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_GT (dtls.read (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); +} + +TEST_F (DtlsSocket, readExactly) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_EQ (dtls.readExactly (data, sizeof (data)), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.readExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test waitReadyWrite method. + */ +TEST_F (DtlsSocket, waitReadyWrite) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + + ASSERT_FALSE (dtls.waitReadyWrite (_timeout)); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + if (dtls.connect ({_hostv4, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); + if (dtls.handshake () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitHandshake (_timeout)) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyWrite (_timeout)) << join::lastError.message (); + if (dtls.shutdown () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitShutdown (_timeout)) << join::lastError.message (); + if (dtls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test write method. + */ +TEST_F (DtlsSocket, write) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_EQ (dtls.write (data, sizeof (data)), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_GT (dtls.write (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); +} + +TEST_F (DtlsSocket, writeExactly) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_EQ (dtls.writeExactly (data, sizeof (data)), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test setMode method. + */ +TEST_F (DtlsSocket, setMode) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + + ASSERT_EQ (dtls.open (), 0) << join::lastError.message (); + + int flags = ::fcntl (dtls.handle (), F_GETFL, 0); + ASSERT_TRUE (flags & O_NONBLOCK); + + dtls.setMode (Udp::Socket::Blocking); + flags = ::fcntl (dtls.handle (), F_GETFL, 0); + ASSERT_FALSE (flags & O_NONBLOCK); + + dtls.setMode (Udp::Socket::NonBlocking); + flags = ::fcntl (dtls.handle (), F_GETFL, 0); + ASSERT_TRUE (flags & O_NONBLOCK); + + dtls.close (); +} + +/** + * @brief Test setOption method. + */ +TEST_F (DtlsSocket, setOption) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + + ASSERT_EQ (dtls.setOption (Udp::Socket::RcvBuffer, 1500), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + + ASSERT_EQ (dtls.open (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::NoDelay, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (dtls.setOption (Udp::Socket::KeepAlive, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::KeepIdle, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (dtls.setOption (Udp::Socket::KeepIntvl, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (dtls.setOption (Udp::Socket::KeepCount, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (dtls.setOption (Udp::Socket::SndBuffer, 1500), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::RcvBuffer, 1500), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::TimeStamp, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::ReuseAddr, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::ReusePort, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::Broadcast, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::Ttl, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::MulticastLoop, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::MulticastTtl, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::PathMtuDiscover, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::RcvError, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::AuxData, 1), -1); + ASSERT_EQ (join::lastError, std::errc::no_protocol_option); + dtls.close (); + + ASSERT_EQ (dtls.open (Udp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::NoDelay, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (dtls.setOption (Udp::Socket::KeepAlive, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::KeepIdle, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (dtls.setOption (Udp::Socket::KeepIntvl, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (dtls.setOption (Udp::Socket::KeepCount, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (dtls.setOption (Udp::Socket::SndBuffer, 1500), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::RcvBuffer, 1500), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::TimeStamp, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::ReuseAddr, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::ReusePort, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::Broadcast, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::Ttl, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::MulticastLoop, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::MulticastTtl, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::PathMtuDiscover, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::RcvError, 1), 0) << join::lastError.message (); + ASSERT_EQ (dtls.setOption (Udp::Socket::AuxData, 1), -1); + ASSERT_EQ (join::lastError, std::errc::no_protocol_option); + dtls.close (); +} + +/** + * @brief Test localEndpoint method. + */ +TEST_F (DtlsSocket, localEndpoint) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_EQ (dtls.localEndpoint (), Udp::Endpoint{}); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.localEndpoint (), Udp::Endpoint (_hostv4, uint16_t (_port + 1))) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test remoteEndpoint method. + */ +TEST_F (DtlsSocket, remoteEndpoint) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_EQ (dtls.remoteEndpoint (), Udp::Endpoint{}); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.remoteEndpoint (), Udp::Endpoint (_hostv4, _port)) << join::lastError.message (); + dtls.close (); +} + +/** + * @brief Test opened method. + */ +TEST_F (DtlsSocket, opened) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_FALSE (dtls.opened ()); + ASSERT_EQ (dtls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.opened ()); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.opened ()); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.opened ()); + dtls.close (); + ASSERT_FALSE (dtls.opened ()); +} + +/** + * @brief Test connected method. + */ +TEST_F (DtlsSocket, connected) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_FALSE (dtls.connected ()); + ASSERT_EQ (dtls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_FALSE (dtls.connected ()); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.connected ()); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + ASSERT_FALSE (dtls.connected ()); + dtls.close (); + ASSERT_FALSE (dtls.connected ()); +} + +/** + * @brief Test encrypted method. + */ +TEST_F (DtlsSocket, encrypted) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_FALSE (dtls.encrypted ()); + ASSERT_EQ (dtls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_FALSE (dtls.encrypted ()); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_FALSE (dtls.encrypted ()); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (dtls.encrypted ()); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_FALSE (dtls.encrypted ()); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + ASSERT_FALSE (dtls.encrypted ()); + dtls.close (); + ASSERT_FALSE (dtls.encrypted ()); +} + +/** + * @brief Test family method. + */ +TEST_F (DtlsSocket, family) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_EQ (dtls.family (), AF_INET); + + ASSERT_EQ (dtls.bind (IpAddress (AF_INET6)), 0) << join::lastError.message (); + ASSERT_EQ (dtls.family (), AF_INET6); + dtls.close (); + + ASSERT_EQ (dtls.bind (IpAddress (AF_INET)), 0) << join::lastError.message (); + ASSERT_EQ (dtls.family (), AF_INET); + dtls.close (); +} + +/** + * @brief Test type method. + */ +TEST_F (DtlsSocket, type) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + + ASSERT_EQ (dtls.type (), SOCK_DGRAM); +} + +/** + * @brief Test protocol method. + */ +TEST_F (DtlsSocket, protocol) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + + ASSERT_EQ (dtls.protocol (), IPPROTO_UDP); +} + +/** + * @brief Test handle method. + */ +TEST_F (DtlsSocket, handle) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_EQ (dtls.handle (), -1); + ASSERT_EQ (dtls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_GT (dtls.handle (), -1); + ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_GT (dtls.handle (), -1); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + ASSERT_GT (dtls.handle (), -1); + dtls.close (); + ASSERT_EQ (dtls.handle (), -1); +} + +/** + * @brief Test mtu method. + */ +TEST_F (DtlsSocket, mtu) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx, Udp::Socket::Blocking); + + ASSERT_EQ (dtls.mtu (), -1); + ASSERT_EQ (dtls.connect ({"127.0.0.1", _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_NE (dtls.mtu (), -1) << join::lastError.message (); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + ASSERT_NE (dtls.mtu (), -1) << join::lastError.message (); + dtls.close (); + ASSERT_EQ (dtls.mtu (), -1); + + ASSERT_EQ (dtls.mtu (), -1); + ASSERT_EQ (dtls.connect ({"::1", _port}), 0) << join::lastError.message (); + ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); + ASSERT_NE (dtls.mtu (), -1) << join::lastError.message (); + ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); + ASSERT_NE (dtls.mtu (), -1) << join::lastError.message (); + dtls.close (); + ASSERT_EQ (dtls.mtu (), -1); +} + +/** + * @brief Test cert verification. + */ +TEST_F (DtlsSocket, verify) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + Udp::Endpoint endpoint{_hostv4, _port}; + + ctx.setVerify (false); + TlsWrapper dtls1 (ctx, Udp::Socket::Blocking); + ASSERT_EQ (dtls1.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls1.handshake (), 0) << join::lastError.message (); + ASSERT_EQ (dtls1.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls1.disconnect (), 0) << join::lastError.message (); + dtls1.close (); + + ctx.setVerify (true, 0); + TlsWrapper dtls2 (ctx, Udp::Socket::Blocking); + ASSERT_EQ (dtls2.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls2.handshake (), -1); + ASSERT_EQ (dtls2.disconnect (), 0) << join::lastError.message (); + dtls2.close (); + + ctx.setVerify (true, 1); + TlsWrapper dtls3 (ctx, Udp::Socket::Blocking); + ASSERT_EQ (dtls3.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls3.handshake (), -1); + ASSERT_EQ (dtls3.disconnect (), 0) << join::lastError.message (); + dtls3.close (); + + endpoint.hostname ("localhost"); + + ctx.setVerify (true, 0); + TlsWrapper dtls4 (ctx, Udp::Socket::Blocking); + ASSERT_EQ (dtls4.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls4.handshake (), -1); + ASSERT_EQ (dtls4.disconnect (), 0) << join::lastError.message (); + dtls4.close (); + + ctx.setVerify (true, 1); + TlsWrapper dtls5 (ctx, Udp::Socket::Blocking); + ASSERT_EQ (dtls5.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls5.handshake (), -1); + ASSERT_EQ (dtls5.disconnect (), 0) << join::lastError.message (); + dtls5.close (); + + ASSERT_EQ (ctx.setCaFile (_rootcert), 0) << join::lastError.message (); + + ctx.setVerify (true, 0); + TlsWrapper dtls6 (ctx, Udp::Socket::Blocking); + ASSERT_EQ (dtls6.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls6.handshake (), -1); + ASSERT_EQ (dtls6.disconnect (), 0) << join::lastError.message (); + dtls6.close (); + + ctx.setVerify (true, 1); + TlsWrapper dtls7 (ctx, Udp::Socket::Blocking); + ASSERT_EQ (dtls7.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls7.handshake (), 0) << join::lastError.message (); + ASSERT_EQ (dtls7.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (dtls7.disconnect (), 0) << join::lastError.message (); + dtls7.close (); +} + +/** + * @brief Test is lower method. + */ +TEST_F (DtlsSocket, isLower) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls1 (ctx), dtls2 (ctx); + + ASSERT_EQ (dtls1.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls2.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + if (dtls1.handle () < dtls2.handle ()) + { + ASSERT_TRUE (dtls1 < dtls2); + } + else + { + ASSERT_TRUE (dtls2 < dtls1); + } + dtls1.close (); + dtls2.close (); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + join::initializeOpenSSL (); + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/crypto/tests/tlscontext_test.cpp b/crypto/tests/tlscontext_test.cpp new file mode 100644 index 00000000..d0b52cde --- /dev/null +++ b/crypto/tests/tlscontext_test.cpp @@ -0,0 +1,418 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include +#include + +// Libraries. +#include + +// C++. +#include + +using join::Errc; +using join::TlsContext; + +/** + * @brief Class used to test the TLS context API. + */ +class TlsContextTest : public ::testing::Test +{ +public: + /** + * @brief set up test case. + */ + static void SetUpTestCase () + { + std::ofstream rootCertFile (_root); + if (rootCertFile.is_open ()) + { + rootCertFile << "-----BEGIN CERTIFICATE-----" << std::endl; + rootCertFile << "MIIChjCCAisCFBuHxbqMUGyl7OQUQcoRg3pOBJF+MAoGCCqGSM49BAMCMIHEMQsw" << std::endl; + rootCertFile << "CQYDVQQGEwJGUjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVz" << std::endl; + rootCertFile << "MRcwFQYDVQQKDA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdv" << std::endl; + rootCertFile << "cmsgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3" << std::endl; + rootCertFile << "b3JrLm5ldDEoMCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5l" << std::endl; + rootCertFile << "dDAeFw0yMjA3MDUxNjMxMTZaFw0zMjA3MDIxNjMxMTZaMIHEMQswCQYDVQQGEwJG" << std::endl; + rootCertFile << "UjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVzMRcwFQYDVQQK" << std::endl; + rootCertFile << "DA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdvcmsgQ2VydGlm" << std::endl; + rootCertFile << "aWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3b3JrLm5ldDEo" << std::endl; + rootCertFile << "MCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5ldDBZMBMGByqG" << std::endl; + rootCertFile << "SM49AgEGCCqGSM49AwEHA0IABASk0zCrKtXQi0Ycx+Anx+VWv8gncbPmNQ1yutii" << std::endl; + rootCertFile << "gQjP2mF9NIqlxpcKNuE/6DDnfSzCEDhFyvGiK0NJ1C3RBowwCgYIKoZIzj0EAwID" << std::endl; + rootCertFile << "SQAwRgIhAIFqdbxTb5kRjy4UY0N205ZEhHSMK89p2oUyn4iNbXH2AiEAtmV1UyRX" << std::endl; + rootCertFile << "DIAGr/F+1SwQMPoJzSQxZ7NdxjNgW286e9Q=" << std::endl; + rootCertFile << "-----END CERTIFICATE-----" << std::endl; + rootCertFile.close (); + } + + mkdir (_certPath.c_str (), 0777); + + std::ofstream certFile (_certFile); + if (certFile.is_open ()) + { + certFile << "-----BEGIN CERTIFICATE-----" << std::endl; + certFile << "MIIDgDCCAyagAwIBAgIUR3ZIuKMt0BdaOZQnPwhSMR9qzfgwCgYIKoZIzj0EAwIw" << std::endl; + certFile << "gcQxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nh" << std::endl; + certFile << "c3RyZXMxFzAVBgNVBAoMDkpvaW4gRnJhbWV3b3JrMS0wKwYDVQQLDCRKb2luIEZy" << std::endl; + certFile << "YW1ld29yayBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHTAbBgNVBAMMFGNhLmpvaW5m" << std::endl; + certFile << "cmFtZXdvcmsubmV0MSgwJgYJKoZIhvcNAQkBFhlzdXBwb3J0QGpvaW5mcmFtZXdv" << std::endl; + certFile << "cmsubmV0MB4XDTIyMDcwNzEyMTIxMFoXDTMyMDcwNDEyMTIxMFowgagxCzAJBgNV" << std::endl; + certFile << "BAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nhc3RyZXMxFzAV" << std::endl; + certFile << "BgNVBAoMDkpvaW4gRnJhbWV3b3JrMRswGQYDVQQLDBJKb2luIEZyYW1ld29yayBE" << std::endl; + certFile << "ZXYxEzARBgNVBAMMCmxvY2FsaG9zdC4xKDAmBgkqhkiG9w0BCQEWGXN1cHBvcnRA" << std::endl; + certFile << "am9pbmZyYW1ld29yay5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" << std::endl; + certFile << "AQDSNtw5zEoJFPf6Rl0Y1n8BQfE0YTPCELvFAeioUfj8CAnUleHL9pwAEFg6kgoG" << std::endl; + certFile << "hvwto5/yWGPUqNNfe3xbFTJcHgMhgtjqy5H6sYDkTi3kYIIMBfTHr8NI7HWE8Nz1" << std::endl; + certFile << "qU1snjtERnkoLilIZf/2BojNVMtHC1H316WbMicXS0v7HQo3lv6PYSana9Q9ow9O" << std::endl; + certFile << "2/FiW5qq1eOhI1ZedRanX+bl0jHWCd3WsI87+5bTaQrfetdHTOmav6O17Iq9FiTh" << std::endl; + certFile << "Sg9fbM3s2Hw15kI+mws029dhcwXs5sYY+NgtrQwjR5qH+54BdUaPwQfl/KyulfEl" << std::endl; + certFile << "TJykJ+3w6MorxUr55F68uBNbAgMBAAGjRTBDMAsGA1UdDwQEAwIF4DAdBgNVHSUE" << std::endl; + certFile << "FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwFQYDVR0RBA4wDIIKbG9jYWxob3N0LjAK" << std::endl; + certFile << "BggqhkjOPQQDAgNIADBFAiA120ufIbhcw7BJQ1L6WudDdW2mHrVXvdgeOzVGgz1d" << std::endl; + certFile << "iAIhAMm/sWI3yzb2IMPffxWKYusWEQE2hZvs24ESSC/ZZ0s+" << std::endl; + certFile << "-----END CERTIFICATE-----" << std::endl; + certFile.close (); + } + + [[maybe_unused]] int result; + result = std::system (std::string ("/usr/bin/c_rehash " + _certPath).c_str ()); + + std::ofstream keyFile (_key); + if (keyFile.is_open ()) + { + keyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; + keyFile << "MIIEowIBAAKCAQEA0jbcOcxKCRT3+kZdGNZ/AUHxNGEzwhC7xQHoqFH4/AgJ1JXh" << std::endl; + keyFile << "y/acABBYOpIKBob8LaOf8lhj1KjTX3t8WxUyXB4DIYLY6suR+rGA5E4t5GCCDAX0" << std::endl; + keyFile << "x6/DSOx1hPDc9alNbJ47REZ5KC4pSGX/9gaIzVTLRwtR99elmzInF0tL+x0KN5b+" << std::endl; + keyFile << "j2Emp2vUPaMPTtvxYluaqtXjoSNWXnUWp1/m5dIx1gnd1rCPO/uW02kK33rXR0zp" << std::endl; + keyFile << "mr+jteyKvRYk4UoPX2zN7Nh8NeZCPpsLNNvXYXMF7ObGGPjYLa0MI0eah/ueAXVG" << std::endl; + keyFile << "j8EH5fysrpXxJUycpCft8OjKK8VK+eRevLgTWwIDAQABAoIBAAzdlK7o5OMXaHHl" << std::endl; + keyFile << "2o7Jme5Oxd9pz4wiEAvnqQCcO7vZFhjvr2kXR8btOSkkhP6PRmHYsNJZPIroZj9i" << std::endl; + keyFile << "xGKisnlW0OQ9KN995ApO0M+oRUDD81GfD7Mk+7O73Rls0GksmnN6X7A3C/U8lgQ7" << std::endl; + keyFile << "UeYR0k+Wz/YiKDsd9KHB+QiA8D6HFQ9I8Y2P97KOcYnxXZfSwNm+ENNU3wShZOl2" << std::endl; + keyFile << "ZYJJ4DE+5m2SwZ6g8b5Zre4cDbOduwuz/jXzjy2tAZBlTS4DVpYlhd14z+ssUWiu" << std::endl; + keyFile << "AdS/nqSF7Obj0TRhoGNfrkisFzV4itavQ5DKGj/6hjueIJVLteUOzcCeg26YosNy" << std::endl; + keyFile << "QzZSjOECgYEA7y3InEoh93/4HZCZmdwN8KfZtqirX0t966FntgAT8RkIs+KvNS8B" << std::endl; + keyFile << "m3RfNLa/EuDt5zTmHRGx+oeN+17i9QQjKWcR0NnJ6aSZbvJByj3yKxLF9XVllzp/" << std::endl; + keyFile << "vHSSyB264RoKIrWmFN6cCO4u4h9ZPY75pASWBCDMdnGK8axAcqAnlqsCgYEA4P+Y" << std::endl; + keyFile << "FF9RW4rhrVU4dpXSfcr6vOwqfp9F9vhTVL0JS/SLOFoJNNpS9Rnq3pVLEuKyCphd" << std::endl; + keyFile << "3nk9VFfoRygmMaGBvwGaXZPPvosoaIUgOdTt7KIfSHPichBEVxRuWCrtTGGkG0ok" << std::endl; + keyFile << "s/RPHhvxZE267vsVj1PktK8Yr5Ba0AL2ycztNhECgYB5OAwHYe8LIBlg6otelk+e" << std::endl; + keyFile << "W4OU9rE8L+eWx4vniuyQce6eNNI1syguYHFsJv56E/OfDYlezDwWzCLidnmyUjF7" << std::endl; + keyFile << "51f5MJgLyTdWKoO7e1/EAtS/jYs6dRSOL8rAj4jKU0c1xjhxNU2BnS23vsmc0Fyn" << std::endl; + keyFile << "iwd4+iKGGQ+hYnqbXZ4S1wKBgD/3an0gPDkSWua0e8D7B0TMGEztt4cYMQPtxYMp" << std::endl; + keyFile << "2yLE+2+h6UwlZcBZBfUR7K4J1SQ9/THqtgzskRTpzTH/AKwVAJXqF/3MAkj00Byg" << std::endl; + keyFile << "9KN50/r9NzvGdCdtn5FhYuV8PPOlOJoQsw2UVCR4FNUsfQyqhTL5NMN0/tx0e0UU" << std::endl; + keyFile << "BbyBAoGBANu5ifByauVELH8UEl5rXRu1S9iAVV+Bc5jboXwc4VxJtEyomGJ7+YdL" << std::endl; + keyFile << "5c9LFV+STUp7CE12uSXQZTQM0tEjPinLntRinNzu9tIHR1vy7FZHEwMFIgB4VTY7" << std::endl; + keyFile << "ALRYv1/QpTuywpNUFRS15JkfGNf5JIkrUEWLgkX3OVCBsRGHUugy" << std::endl; + keyFile << "-----END RSA PRIVATE KEY-----" << std::endl; + keyFile.close (); + } + + std::ofstream invalidKeyFile (_invalidKey); + if (invalidKeyFile.is_open ()) + { + invalidKeyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; + invalidKeyFile << "MIIEowIBAAKCAQEA2Q0DOyG039uVMuxNnZ5fpfOcvXXOTguST1QR6eLVkdG7OKpM" << std::endl; + invalidKeyFile << "nc9K597jx1syT1q+SwFcykMtvWxCfD8BR7bcLILeO6z+HlRfvjOhUiHaX/KCaTN8" << std::endl; + invalidKeyFile << "l7OJOgmUlL0FhQ1SXxw7KCSGd+rgu1iHwjFDDkj/tG24ashdmNt+DYdeoJu2mzgw" << std::endl; + invalidKeyFile << "tEASfG9VjqBR7ni4Hg/sRpwXvEK5nI1JSLyZbcPCxGlBRdB8hMdny/VW+SBwKD2/" << std::endl; + invalidKeyFile << "ivpVJLulw2oniSIcCCcr9d+ERY4XrO71UsiACwPxfdEtbG0KrZfpK91k7vl64DHM" << std::endl; + invalidKeyFile << "CeTQPKRZm+LDKOUfv/eTF9F6GY4Dpw2LMwLM5QIDAQABAoIBABjV91etzK+Mxa61" << std::endl; + invalidKeyFile << "AVCWzaUEkhvPvhKKGmy/VulnTj7IO98JBYlNLeoIRBIMql4QKRQWDNMMCtDQ8W6c" << std::endl; + invalidKeyFile << "Gv5kux7QvrMfYViBGQ9/gucN/pnZ+vgkrw4AuiQM8pZuZpJJ6vH9HfvC6iwQkTR+" << std::endl; + invalidKeyFile << "tdIPpvecfL3djCuTz7ns66iKo9ZGpRE6emTBynr8og/oqD8Vw5bW+JJ+AJ3IqZf4" << std::endl; + invalidKeyFile << "NslNist7d5FZ5N/+nxWyBUcFglP7bZzb/raOVc/flrYIeDy72asnWOYbDTPzMyH1" << std::endl; + invalidKeyFile << "dfaox6QKZtA5NdO9x4aHHGgAz8BTgqs7LvxPwoH+XF1dDCsb3kIeQxHTfcc1opMw" << std::endl; + invalidKeyFile << "atxpgwECgYEA8Zq/7Z3tKcBlMz4XNKWWvaDxhBUIS62tGeLJ2spLRFvkL1ixnjcK" << std::endl; + invalidKeyFile << "72YWOwDpoINEWa8AhAhM6afE9VxrupSGg+C9uALaJ8HTWTP6u6/F8sbsYaoWHyA/" << std::endl; + invalidKeyFile << "k/8/nFEr43ciKUjBhMHB42vYidAgiOvDVXc+/k7HIMQfl/vyp32ecEECgYEA5fu9" << std::endl; + invalidKeyFile << "ePLh55TYbXe8SCL0hsZcC8Q/ioT/0GJ6uevGb0lw3XAa+HC6//upu90T7ZOIqysc" << std::endl; + invalidKeyFile << "aAqln7ZEeCfvXI/3YJyJ2RWatD+2itECbd0WV2/JflO/OAzDSSFvpxxmwIzccIeA" << std::endl; + invalidKeyFile << "UNuNcQGD8HDwFzU+sULvF82yuwMt1syPd/mns6UCgYAviqP5vfnNHW7MhotKcMsY" << std::endl; + invalidKeyFile << "xXLA6uKXAbXuQhI2W1g0O2DLcEiDOZGNSilVsvhF/Y6VlzoiwP9hewHmxijsrg1K" << std::endl; + invalidKeyFile << "Jg8vBmCnMhzEkNXl2NC61SnujemMdmwMU03RFKfuOqMePJLX7MiaV75kX/AHAV2O" << std::endl; + invalidKeyFile << "k8hxgk7sw6rz3UACdVWYAQKBgHUu5ScoksS+Cd0VQmF7Nh8qGSKBt2KsS/BxDVmI" << std::endl; + invalidKeyFile << "ck6oHBMomQV340CliaHIjuvh3aRhzhKRQjzz0UVsC8GdNY4LlQ2AvZgUUr2+q78x" << std::endl; + invalidKeyFile << "BL4+nmt43pj/n822dL6wcQaxf2zzDgWlKReojwLHeP5KSgxmL49wZx51CzlEd+HI" << std::endl; + invalidKeyFile << "2pNlAoGBAObdC7woN7jEfdfYz1BhUpmBsIRqW2yLA1DnlK9lfgs2i1w7spzAh2hV" << std::endl; + invalidKeyFile << "djPiKj5vZdcrbaa+SBAnZbFTHyXmAbKbO/iZpSromaZYyCK8NktJu/YxpWZmjnRF" << std::endl; + invalidKeyFile << "2xOadRGCav5fTGzCN/ADLgIo4gIAI2o/UnV/MdaSAdHyIeSrxBAb" << std::endl; + invalidKeyFile << "-----END RSA PRIVATE KEY-----" << std::endl; + invalidKeyFile.close (); + } + } + + /** + * @brief tear down test case. + */ + static void TearDownTestCase () + { + unlink (_root.c_str ()); + unlink (_certFile.c_str ()); + rmdir (_certPath.c_str ()); + unlink (_key.c_str ()); + unlink (_invalidKey.c_str ()); + } + +protected: + /// root certificate. + static const std::string _root; + + /// certificate path. + static const std::string _certPath; + + /// certificate file. + static const std::string _certFile; + + /// private key. + static const std::string _key; + + /// invalid private key. + static const std::string _invalidKey; +}; + +const std::string TlsContextTest::_root = "/tmp/tlscontext_test_root.cert"; +const std::string TlsContextTest::_certPath = "/tmp/certs"; +const std::string TlsContextTest::_certFile = _certPath + "/tlscontext_test.cert"; +const std::string TlsContextTest::_key = "/tmp/tlscontext_test.key"; +const std::string TlsContextTest::_invalidKey = "/tmp/tlscontext_test_invalid.key"; + +/** + * @brief Test constructor. + */ +TEST_F (TlsContextTest, construct) +{ + ASSERT_NO_THROW (TlsContext (TlsContext::Role::TlsClient)); + ASSERT_NO_THROW (TlsContext (TlsContext::Role::TlsServer)); + ASSERT_NO_THROW (TlsContext (TlsContext::Role::DtlsClient)); + ASSERT_NO_THROW (TlsContext (TlsContext::Role::DtlsServer)); +} + +/** + * @brief Test copy. + */ +TEST_F (TlsContextTest, copy) +{ + TlsContext ctx1; + ctx1.setVerify (true, 4); + + TlsContext ctx2 (ctx1); + ASSERT_EQ (ctx2.handle (), ctx1.handle ()); + ASSERT_EQ (ctx2.verify (), true); + ASSERT_EQ (ctx2.depth (), 4); + + ctx2.setVerify (false, -1); + ASSERT_EQ (ctx2.verify (), false); + ASSERT_EQ (ctx1.verify (), true); + + TlsContext ctx3; + ctx3 = ctx1; + ASSERT_EQ (ctx3.handle (), ctx1.handle ()); + ASSERT_EQ (ctx3.verify (), true); + ASSERT_EQ (ctx3.depth (), 4); +} + +TEST_F (TlsContextTest, move) +{ + TlsContext ctx1; + ctx1.setVerify (true, 4); + SSL_CTX* raw = ctx1.handle (); + + TlsContext ctx2 (std::move (ctx1)); + ASSERT_EQ (ctx2.handle (), raw); + ASSERT_EQ (ctx2.verify (), true); + ASSERT_EQ (ctx2.depth (), 4); + ASSERT_EQ (ctx1.handle (), nullptr); + + TlsContext ctx3; + ctx3 = std::move (ctx2); + ASSERT_EQ (ctx3.handle (), raw); + ASSERT_EQ (ctx3.verify (), true); + ASSERT_EQ (ctx3.depth (), 4); + ASSERT_EQ (ctx2.handle (), nullptr); +} + +/** + * @brief Test the setCertificate method. + */ +TEST_F (TlsContextTest, setCertificate) +{ + TlsContext ctx; + + ASSERT_EQ (ctx.setCertificate ("/invalid/cert/path"), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCertificate (_certFile), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCertificate (_certFile, "/invalid/key/path"), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCertificate (_certFile, _invalidKey), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCertificate (_certFile, _key), 0) << join::lastError.message (); +} + +/** + * @brief Test the setCaPath method. + */ +TEST_F (TlsContextTest, setCaPath) +{ + TlsContext ctx; + + ASSERT_EQ (ctx.setCaPath ("/invalid/ca/path"), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCaPath (_certFile), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCaPath (_certPath), 0) << join::lastError.message (); +} + +/** + * @brief Test the setCaFile method. + */ +TEST_F (TlsContextTest, setCaFile) +{ + TlsContext ctx; + + ASSERT_EQ (ctx.setCaFile ("/invalid/ca/file"), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCaFile (_certPath), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCaFile (_certFile), 0) << join::lastError.message (); +} + +/** + * @brief Test the setCipher method. + */ +TEST_F (TlsContextTest, setCipher) +{ + TlsContext ctx; + + ASSERT_EQ (ctx.setCipher ("foo"), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCipher (join::defaultCipher), 0) << join::lastError.message (); +} + +/** + * @brief Test the setCipher_1_3 method. + */ +TEST_F (TlsContextTest, setCipher_1_3) +{ + TlsContext ctx; + + ASSERT_EQ (ctx.setCipher_1_3 ("foo"), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); +} + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +/** + * @brief Test the setCurve method. + */ +TEST_F (TlsContextTest, setCurve) +{ + TlsContext ctx; + + ASSERT_EQ (ctx.setCurve ("foo"), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setCurve (join::defaultCurve), 0) << join::lastError.message (); +} +#endif + +/** + * @brief Test the setAlpnProtocols method. + */ +TEST_F (TlsContextTest, setAlpnProtocols) +{ + TlsContext ctx; + + ASSERT_EQ (ctx.setAlpnProtocols ({""}), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + + ASSERT_EQ (ctx.setAlpnProtocols ({"http/1.1", "h2"}), 0) << join::lastError.message (); +} + +/** + * @brief Test the handle method. + */ +TEST_F (TlsContextTest, handle) +{ + TlsContext ctx; + + ASSERT_NE (ctx.handle (), nullptr); +} + +/** + * @brief Test the verify method. + */ +TEST_F (TlsContextTest, verify) +{ + TlsContext ctx; + + ASSERT_EQ (ctx.verify (), false); + + ASSERT_NO_THROW (ctx.setVerify (true)); + ASSERT_EQ (ctx.verify (), true); + + ASSERT_NO_THROW (ctx.setVerify (false)); + ASSERT_EQ (ctx.verify (), false); +} + +/** + * @brief Test the depth method. + */ +TEST_F (TlsContextTest, depth) +{ + TlsContext ctx; + + ASSERT_EQ (ctx.depth (), -1); + + ASSERT_NO_THROW (ctx.setVerify (true, 8)); + ASSERT_EQ (ctx.depth (), 8); + + ASSERT_NO_THROW (ctx.setVerify (true, -1)); + ASSERT_EQ (ctx.depth (), -1); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + join::initializeOpenSSL (); + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/crypto/tests/tlserror_test.cpp b/crypto/tests/tlserror_test.cpp new file mode 100644 index 00000000..3d5ac2a4 --- /dev/null +++ b/crypto/tests/tlserror_test.cpp @@ -0,0 +1,150 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include + +// Libraries. +#include + +using join::TlsErrc; +using join::TlsCategory; + +/** + * @brief Test name. + */ +TEST (TlsCategory, name) +{ + EXPECT_STREQ (TlsCategory ().name (), "libjoin"); +} + +/** + * @brief Test message. + */ +TEST (TlsCategory, message) +{ + EXPECT_STREQ (TlsCategory ().message (0).c_str (), "success"); + EXPECT_STREQ (TlsCategory ().message (static_cast (TlsErrc::TlsCloseNotifyAlert)).c_str (), + "TLS close notify alert received"); + EXPECT_STREQ (TlsCategory ().message (static_cast (TlsErrc::TlsProtocolError)).c_str (), "TLS protocol error"); +} + +/** + * @brief Test default_error_condition. + */ +TEST (TlsCategory, default_error_condition) +{ + EXPECT_EQ (TlsCategory ().default_error_condition (0).message (), "success"); + EXPECT_EQ (TlsCategory ().default_error_condition (1).message (), "TLS close notify alert received"); + EXPECT_EQ (TlsCategory ().default_error_condition (2).message (), "TLS protocol error"); +} + +/** + * @brief Test equal. + */ +TEST (TlsCategory, equal) +{ + std::error_code error1, error2; + + error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + error2 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + ASSERT_TRUE (error1 == error2); + + error2 = make_error_code (TlsErrc::TlsProtocolError); + ASSERT_FALSE (error1 == error2); + + error1 = make_error_code (TlsErrc::TlsProtocolError); + ASSERT_TRUE (error1 == error2); + + error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + ASSERT_TRUE (error1 == TlsErrc::TlsCloseNotifyAlert); + + error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + ASSERT_FALSE (error1 == TlsErrc::TlsProtocolError); + + error2 = make_error_code (TlsErrc::TlsProtocolError); + ASSERT_TRUE (TlsErrc::TlsProtocolError == error2); + + error2 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + ASSERT_FALSE (TlsErrc::TlsProtocolError == error2); +} + +/** + * @brief Test different. + */ +TEST (TlsCategory, different) +{ + std::error_code error1, error2; + + error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + error2 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + ASSERT_FALSE (error1 != error2); + + error2 = make_error_code (TlsErrc::TlsProtocolError); + ASSERT_TRUE (error1 != error2); + + error1 = make_error_code (TlsErrc::TlsProtocolError); + ASSERT_FALSE (error1 != error2); + + error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + ASSERT_FALSE (error1 != TlsErrc::TlsCloseNotifyAlert); + + error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + ASSERT_TRUE (error1 != TlsErrc::TlsProtocolError); + + error2 = make_error_code (TlsErrc::TlsProtocolError); + ASSERT_FALSE (TlsErrc::TlsProtocolError != error2); + + error2 = make_error_code (TlsErrc::TlsCloseNotifyAlert); + ASSERT_TRUE (TlsErrc::TlsProtocolError != error2); +} + +/** + * @brief Test make_error_code. + */ +TEST (TlsCategory, make_error_code) +{ + auto code = make_error_code (TlsErrc::TlsCloseNotifyAlert); + EXPECT_EQ (code, TlsErrc::TlsCloseNotifyAlert); + EXPECT_STREQ (code.message ().c_str (), "TLS close notify alert received"); +} + +/** + * @brief Test make_error_condition. + */ +TEST (TlsCategory, make_error_condition) +{ + auto code = make_error_condition (TlsErrc::TlsProtocolError); + EXPECT_EQ (code, TlsErrc::TlsProtocolError); + EXPECT_STREQ (code.message ().c_str (), "TLS protocol error"); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/crypto/tests/tlswrapper_test.cpp b/crypto/tests/tlswrapper_test.cpp new file mode 100644 index 00000000..fedd7228 --- /dev/null +++ b/crypto/tests/tlswrapper_test.cpp @@ -0,0 +1,1036 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include +#include +#include + +// Libraries. +#include + +// C++. +#include + +using join::Errc; +using join::IpAddress; +using join::ReactorThread; +using join::EventHandler; +using join::Tcp; +using join::TlsContext; +using join::TlsWrapper; + +/** + * @brief Class used to test the stream TLS wrapper API. + */ +class TlsSocket : public EventHandler, public ::testing::Test +{ +public: + /** + * @brief set up test case. + */ + static void SetUpTestCase () + { + std::ofstream rootCertFile (_rootcert); + if (rootCertFile.is_open ()) + { + rootCertFile << "-----BEGIN CERTIFICATE-----" << std::endl; + rootCertFile << "MIIChjCCAisCFBuHxbqMUGyl7OQUQcoRg3pOBJF+MAoGCCqGSM49BAMCMIHEMQsw" << std::endl; + rootCertFile << "CQYDVQQGEwJGUjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVz" << std::endl; + rootCertFile << "MRcwFQYDVQQKDA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdv" << std::endl; + rootCertFile << "cmsgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3" << std::endl; + rootCertFile << "b3JrLm5ldDEoMCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5l" << std::endl; + rootCertFile << "dDAeFw0yMjA3MDUxNjMxMTZaFw0zMjA3MDIxNjMxMTZaMIHEMQswCQYDVQQGEwJG" << std::endl; + rootCertFile << "UjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVzMRcwFQYDVQQK" << std::endl; + rootCertFile << "DA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdvcmsgQ2VydGlm" << std::endl; + rootCertFile << "aWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3b3JrLm5ldDEo" << std::endl; + rootCertFile << "MCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5ldDBZMBMGByqG" << std::endl; + rootCertFile << "SM49AgEGCCqGSM49AwEHA0IABASk0zCrKtXQi0Ycx+Anx+VWv8gncbPmNQ1yutii" << std::endl; + rootCertFile << "gQjP2mF9NIqlxpcKNuE/6DDnfSzCEDhFyvGiK0NJ1C3RBowwCgYIKoZIzj0EAwID" << std::endl; + rootCertFile << "SQAwRgIhAIFqdbxTb5kRjy4UY0N205ZEhHSMK89p2oUyn4iNbXH2AiEAtmV1UyRX" << std::endl; + rootCertFile << "DIAGr/F+1SwQMPoJzSQxZ7NdxjNgW286e9Q=" << std::endl; + rootCertFile << "-----END CERTIFICATE-----" << std::endl; + rootCertFile.close (); + } + + mkdir (_certPath.c_str (), 0777); + std::ofstream certFile (_certFile); + if (certFile.is_open ()) + { + certFile << "-----BEGIN CERTIFICATE-----" << std::endl; + certFile << "MIIDgDCCAyagAwIBAgIUR3ZIuKMt0BdaOZQnPwhSMR9qzfgwCgYIKoZIzj0EAwIw" << std::endl; + certFile << "gcQxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nh" << std::endl; + certFile << "c3RyZXMxFzAVBgNVBAoMDkpvaW4gRnJhbWV3b3JrMS0wKwYDVQQLDCRKb2luIEZy" << std::endl; + certFile << "YW1ld29yayBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHTAbBgNVBAMMFGNhLmpvaW5m" << std::endl; + certFile << "cmFtZXdvcmsubmV0MSgwJgYJKoZIhvcNAQkBFhlzdXBwb3J0QGpvaW5mcmFtZXdv" << std::endl; + certFile << "cmsubmV0MB4XDTIyMDcwNzEyMTIxMFoXDTMyMDcwNDEyMTIxMFowgagxCzAJBgNV" << std::endl; + certFile << "BAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nhc3RyZXMxFzAV" << std::endl; + certFile << "BgNVBAoMDkpvaW4gRnJhbWV3b3JrMRswGQYDVQQLDBJKb2luIEZyYW1ld29yayBE" << std::endl; + certFile << "ZXYxEzARBgNVBAMMCmxvY2FsaG9zdC4xKDAmBgkqhkiG9w0BCQEWGXN1cHBvcnRA" << std::endl; + certFile << "am9pbmZyYW1ld29yay5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" << std::endl; + certFile << "AQDSNtw5zEoJFPf6Rl0Y1n8BQfE0YTPCELvFAeioUfj8CAnUleHL9pwAEFg6kgoG" << std::endl; + certFile << "hvwto5/yWGPUqNNfe3xbFTJcHgMhgtjqy5H6sYDkTi3kYIIMBfTHr8NI7HWE8Nz1" << std::endl; + certFile << "qU1snjtERnkoLilIZf/2BojNVMtHC1H316WbMicXS0v7HQo3lv6PYSana9Q9ow9O" << std::endl; + certFile << "2/FiW5qq1eOhI1ZedRanX+bl0jHWCd3WsI87+5bTaQrfetdHTOmav6O17Iq9FiTh" << std::endl; + certFile << "Sg9fbM3s2Hw15kI+mws029dhcwXs5sYY+NgtrQwjR5qH+54BdUaPwQfl/KyulfEl" << std::endl; + certFile << "TJykJ+3w6MorxUr55F68uBNbAgMBAAGjRTBDMAsGA1UdDwQEAwIF4DAdBgNVHSUE" << std::endl; + certFile << "FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwFQYDVR0RBA4wDIIKbG9jYWxob3N0LjAK" << std::endl; + certFile << "BggqhkjOPQQDAgNIADBFAiA120ufIbhcw7BJQ1L6WudDdW2mHrVXvdgeOzVGgz1d" << std::endl; + certFile << "iAIhAMm/sWI3yzb2IMPffxWKYusWEQE2hZvs24ESSC/ZZ0s+" << std::endl; + certFile << "-----END CERTIFICATE-----" << std::endl; + certFile.close (); + } + + [[maybe_unused]] int result; + result = std::system (std::string ("/usr/bin/c_rehash " + _certPath).c_str ()); + + std::ofstream keyFile (_key); + if (keyFile.is_open ()) + { + keyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; + keyFile << "MIIEowIBAAKCAQEA0jbcOcxKCRT3+kZdGNZ/AUHxNGEzwhC7xQHoqFH4/AgJ1JXh" << std::endl; + keyFile << "y/acABBYOpIKBob8LaOf8lhj1KjTX3t8WxUyXB4DIYLY6suR+rGA5E4t5GCCDAX0" << std::endl; + keyFile << "x6/DSOx1hPDc9alNbJ47REZ5KC4pSGX/9gaIzVTLRwtR99elmzInF0tL+x0KN5b+" << std::endl; + keyFile << "j2Emp2vUPaMPTtvxYluaqtXjoSNWXnUWp1/m5dIx1gnd1rCPO/uW02kK33rXR0zp" << std::endl; + keyFile << "mr+jteyKvRYk4UoPX2zN7Nh8NeZCPpsLNNvXYXMF7ObGGPjYLa0MI0eah/ueAXVG" << std::endl; + keyFile << "j8EH5fysrpXxJUycpCft8OjKK8VK+eRevLgTWwIDAQABAoIBAAzdlK7o5OMXaHHl" << std::endl; + keyFile << "2o7Jme5Oxd9pz4wiEAvnqQCcO7vZFhjvr2kXR8btOSkkhP6PRmHYsNJZPIroZj9i" << std::endl; + keyFile << "xGKisnlW0OQ9KN995ApO0M+oRUDD81GfD7Mk+7O73Rls0GksmnN6X7A3C/U8lgQ7" << std::endl; + keyFile << "UeYR0k+Wz/YiKDsd9KHB+QiA8D6HFQ9I8Y2P97KOcYnxXZfSwNm+ENNU3wShZOl2" << std::endl; + keyFile << "ZYJJ4DE+5m2SwZ6g8b5Zre4cDbOduwuz/jXzjy2tAZBlTS4DVpYlhd14z+ssUWiu" << std::endl; + keyFile << "AdS/nqSF7Obj0TRhoGNfrkisFzV4itavQ5DKGj/6hjueIJVLteUOzcCeg26YosNy" << std::endl; + keyFile << "QzZSjOECgYEA7y3InEoh93/4HZCZmdwN8KfZtqirX0t966FntgAT8RkIs+KvNS8B" << std::endl; + keyFile << "m3RfNLa/EuDt5zTmHRGx+oeN+17i9QQjKWcR0NnJ6aSZbvJByj3yKxLF9XVllzp/" << std::endl; + keyFile << "vHSSyB264RoKIrWmFN6cCO4u4h9ZPY75pASWBCDMdnGK8axAcqAnlqsCgYEA4P+Y" << std::endl; + keyFile << "FF9RW4rhrVU4dpXSfcr6vOwqfp9F9vhTVL0JS/SLOFoJNNpS9Rnq3pVLEuKyCphd" << std::endl; + keyFile << "3nk9VFfoRygmMaGBvwGaXZPPvosoaIUgOdTt7KIfSHPichBEVxRuWCrtTGGkG0ok" << std::endl; + keyFile << "s/RPHhvxZE267vsVj1PktK8Yr5Ba0AL2ycztNhECgYB5OAwHYe8LIBlg6otelk+e" << std::endl; + keyFile << "W4OU9rE8L+eWx4vniuyQce6eNNI1syguYHFsJv56E/OfDYlezDwWzCLidnmyUjF7" << std::endl; + keyFile << "51f5MJgLyTdWKoO7e1/EAtS/jYs6dRSOL8rAj4jKU0c1xjhxNU2BnS23vsmc0Fyn" << std::endl; + keyFile << "iwd4+iKGGQ+hYnqbXZ4S1wKBgD/3an0gPDkSWua0e8D7B0TMGEztt4cYMQPtxYMp" << std::endl; + keyFile << "2yLE+2+h6UwlZcBZBfUR7K4J1SQ9/THqtgzskRTpzTH/AKwVAJXqF/3MAkj00Byg" << std::endl; + keyFile << "9KN50/r9NzvGdCdtn5FhYuV8PPOlOJoQsw2UVCR4FNUsfQyqhTL5NMN0/tx0e0UU" << std::endl; + keyFile << "BbyBAoGBANu5ifByauVELH8UEl5rXRu1S9iAVV+Bc5jboXwc4VxJtEyomGJ7+YdL" << std::endl; + keyFile << "5c9LFV+STUp7CE12uSXQZTQM0tEjPinLntRinNzu9tIHR1vy7FZHEwMFIgB4VTY7" << std::endl; + keyFile << "ALRYv1/QpTuywpNUFRS15JkfGNf5JIkrUEWLgkX3OVCBsRGHUugy" << std::endl; + keyFile << "-----END RSA PRIVATE KEY-----" << std::endl; + keyFile.close (); + } + + std::ofstream invalidKeyFile (_invalidKey); + if (invalidKeyFile.is_open ()) + { + invalidKeyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; + invalidKeyFile << "MIIEowIBAAKCAQEA2Q0DOyG039uVMuxNnZ5fpfOcvXXOTguST1QR6eLVkdG7OKpM" << std::endl; + invalidKeyFile << "nc9K597jx1syT1q+SwFcykMtvWxCfD8BR7bcLILeO6z+HlRfvjOhUiHaX/KCaTN8" << std::endl; + invalidKeyFile << "l7OJOgmUlL0FhQ1SXxw7KCSGd+rgu1iHwjFDDkj/tG24ashdmNt+DYdeoJu2mzgw" << std::endl; + invalidKeyFile << "tEASfG9VjqBR7ni4Hg/sRpwXvEK5nI1JSLyZbcPCxGlBRdB8hMdny/VW+SBwKD2/" << std::endl; + invalidKeyFile << "ivpVJLulw2oniSIcCCcr9d+ERY4XrO71UsiACwPxfdEtbG0KrZfpK91k7vl64DHM" << std::endl; + invalidKeyFile << "CeTQPKRZm+LDKOUfv/eTF9F6GY4Dpw2LMwLM5QIDAQABAoIBABjV91etzK+Mxa61" << std::endl; + invalidKeyFile << "AVCWzaUEkhvPvhKKGmy/VulnTj7IO98JBYlNLeoIRBIMql4QKRQWDNMMCtDQ8W6c" << std::endl; + invalidKeyFile << "Gv5kux7QvrMfYViBGQ9/gucN/pnZ+vgkrw4AuiQM8pZuZpJJ6vH9HfvC6iwQkTR+" << std::endl; + invalidKeyFile << "tdIPpvecfL3djCuTz7ns66iKo9ZGpRE6emTBynr8og/oqD8Vw5bW+JJ+AJ3IqZf4" << std::endl; + invalidKeyFile << "NslNist7d5FZ5N/+nxWyBUcFglP7bZzb/raOVc/flrYIeDy72asnWOYbDTPzMyH1" << std::endl; + invalidKeyFile << "dfaox6QKZtA5NdO9x4aHHGgAz8BTgqs7LvxPwoH+XF1dDCsb3kIeQxHTfcc1opMw" << std::endl; + invalidKeyFile << "atxpgwECgYEA8Zq/7Z3tKcBlMz4XNKWWvaDxhBUIS62tGeLJ2spLRFvkL1ixnjcK" << std::endl; + invalidKeyFile << "72YWOwDpoINEWa8AhAhM6afE9VxrupSGg+C9uALaJ8HTWTP6u6/F8sbsYaoWHyA/" << std::endl; + invalidKeyFile << "k/8/nFEr43ciKUjBhMHB42vYidAgiOvDVXc+/k7HIMQfl/vyp32ecEECgYEA5fu9" << std::endl; + invalidKeyFile << "ePLh55TYbXe8SCL0hsZcC8Q/ioT/0GJ6uevGb0lw3XAa+HC6//upu90T7ZOIqysc" << std::endl; + invalidKeyFile << "aAqln7ZEeCfvXI/3YJyJ2RWatD+2itECbd0WV2/JflO/OAzDSSFvpxxmwIzccIeA" << std::endl; + invalidKeyFile << "UNuNcQGD8HDwFzU+sULvF82yuwMt1syPd/mns6UCgYAviqP5vfnNHW7MhotKcMsY" << std::endl; + invalidKeyFile << "xXLA6uKXAbXuQhI2W1g0O2DLcEiDOZGNSilVsvhF/Y6VlzoiwP9hewHmxijsrg1K" << std::endl; + invalidKeyFile << "Jg8vBmCnMhzEkNXl2NC61SnujemMdmwMU03RFKfuOqMePJLX7MiaV75kX/AHAV2O" << std::endl; + invalidKeyFile << "k8hxgk7sw6rz3UACdVWYAQKBgHUu5ScoksS+Cd0VQmF7Nh8qGSKBt2KsS/BxDVmI" << std::endl; + invalidKeyFile << "ck6oHBMomQV340CliaHIjuvh3aRhzhKRQjzz0UVsC8GdNY4LlQ2AvZgUUr2+q78x" << std::endl; + invalidKeyFile << "BL4+nmt43pj/n822dL6wcQaxf2zzDgWlKReojwLHeP5KSgxmL49wZx51CzlEd+HI" << std::endl; + invalidKeyFile << "2pNlAoGBAObdC7woN7jEfdfYz1BhUpmBsIRqW2yLA1DnlK9lfgs2i1w7spzAh2hV" << std::endl; + invalidKeyFile << "djPiKj5vZdcrbaa+SBAnZbFTHyXmAbKbO/iZpSromaZYyCK8NktJu/YxpWZmjnRF" << std::endl; + invalidKeyFile << "2xOadRGCav5fTGzCN/ADLgIo4gIAI2o/UnV/MdaSAdHyIeSrxBAb" << std::endl; + invalidKeyFile << "-----END RSA PRIVATE KEY-----" << std::endl; + invalidKeyFile.close (); + } + } + + /** + * @brief tear down test case. + */ + static void TearDownTestCase () + { + unlink (_rootcert.c_str ()); + unlink (_certFile.c_str ()); + rmdir (_certPath.c_str ()); + unlink (_key.c_str ()); + unlink (_invalidKey.c_str ()); + } + +protected: + /** + * @brief Sets up the test fixture. + */ + void SetUp () override + { + ASSERT_EQ (_tlsContext.setCertificate (_certFile, _key), 0); + ASSERT_EQ (_tlsContext.setCipher (join::defaultCipher), 0); + ASSERT_EQ (_tlsContext.setCipher_1_3 (join::defaultCipher_1_3), 0); + + ASSERT_EQ (_acceptor.create ({IpAddress::ipv6Wildcard, _port}), 0); + ASSERT_EQ (ReactorThread::reactor ().addHandler (_acceptor.handle (), this), 0); + } + + /** + * @brief Tears down the test fixture. + */ + void TearDown () override + { + ASSERT_EQ (ReactorThread::reactor ().delHandler (_acceptor.handle ()), 0) << join::lastError.message (); + _acceptor.close (); + } + + /** + * @brief method called when data are ready to be read on handle. + * @param fd file descriptor. + */ + virtual void onReadable ([[maybe_unused]] int fd) override + { + Tcp::Socket tcp = _acceptor.accept (); + if (tcp.connected ()) + { + TlsWrapper tls (std::move (tcp), _tlsContext); + tls.waitHandshake (_timeout); + + char buf[1024]; + for (;;) + { + int nread = tls.read (buf, sizeof (buf)); + if (nread == -1) + { + if (join::lastError == Errc::TemporaryError) + { + if (tls.waitReadyRead (_timeout)) + continue; + } + break; + } + tls.writeExactly (buf, nread); + } + + tls.waitShutdown (_timeout); + tls.waitDisconnected (_timeout); + tls.close (); + } + } + + /// TLS context serveur. + TlsContext _tlsContext{TlsContext::Role::TlsServer}; + + /// acceptor. + Tcp::Acceptor _acceptor; + + /// host. + static const std::string _hostv4; + static const std::string _hostv6; + + /// port. + static const uint16_t _port; + static const uint16_t _invalid_port; + + /// timeout. + static const int _timeout; + + /// root certificate. + static const std::string _rootcert; + + /// certificate path. + static const std::string _certPath; + + /// certificate file. + static const std::string _certFile; + + /// private key. + static const std::string _key; + + /// invalid private key. + static const std::string _invalidKey; +}; + +const std::string TlsSocket::_hostv4 = "127.0.0.1"; +const std::string TlsSocket::_hostv6 = "::1"; +const uint16_t TlsSocket::_port = 5000; +const uint16_t TlsSocket::_invalid_port = 5032; +const int TlsSocket::_timeout = 1000; +const std::string TlsSocket::_rootcert = "/tmp/tlssocket_test_root.cert"; +const std::string TlsSocket::_certPath = "/tmp/certs"; +const std::string TlsSocket::_certFile = _certPath + "/tlssocket_test.cert"; +const std::string TlsSocket::_key = "/tmp/tlssocket_test.key"; +const std::string TlsSocket::_invalidKey = "/tmp/tlssocket_test_invalid.key"; + +/** + * @brief Test move. + */ +TEST_F (TlsSocket, move) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls1 (ctx, Tcp::Socket::Blocking), tls2 (ctx); + + ASSERT_EQ (tls1.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_TRUE (tls1.connected ()); + ASSERT_EQ (tls1.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (tls1.encrypted ()); + tls2 = std::move (tls1); + ASSERT_TRUE (tls2.connected ()); + ASSERT_TRUE (tls2.encrypted ()); + ASSERT_EQ (tls2.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls2.disconnect (), 0) << join::lastError.message (); + tls2.close (); +} + +/** + * @brief Test open method. + */ +TEST_F (TlsSocket, open) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx); + + ASSERT_EQ (tls.open (Tcp::v4 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tcp::v4 ()), -1); + ASSERT_EQ (join::lastError, Errc::InUse); + tls.close (); + + ASSERT_EQ (tls.open (Tcp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tcp::v6 ()), -1); + ASSERT_EQ (join::lastError, Errc::InUse); + tls.close (); +} + +/** + * @brief Test close method. + */ +TEST_F (TlsSocket, close) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_FALSE (tls.opened ()); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_TRUE (tls.opened ()); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + ASSERT_TRUE (tls.opened ()); + tls.close (); + ASSERT_FALSE (tls.opened ()); +} + +/** + * @brief Test bind method. + */ +TEST_F (TlsSocket, bind) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.bind (_hostv4), -1); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); + + ASSERT_EQ (tls.bind (_hostv4), 0) << join::lastError.message (); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test bindToDevice method. + */ +TEST_F (TlsSocket, bindToDevice) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (tls.bindToDevice ("lo"), -1); + + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.bindToDevice ("lo"), -1); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); + + ASSERT_EQ (tls.open (Tcp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.bindToDevice ("lo"), 0) << join::lastError.message (); + ASSERT_EQ (tls.connect ({_hostv6, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); + + ASSERT_EQ (tls.bindToDevice ("foo"), -1); +} + +/** + * @brief Test connect method. + */ +TEST_F (TlsSocket, connect) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (tls.connect ({"255.255.255.255", _port}), -1); + + ASSERT_EQ (tls.connect ({_hostv4, _invalid_port}), -1); + + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.connect ({_hostv4, _port}), -1); + ASSERT_EQ (join::lastError, Errc::InUse); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test waitConnected method. + */ +TEST_F (TlsSocket, waitConnected) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx); + + ASSERT_FALSE (tls.waitConnected (_timeout)); + if (tls.connect ({_hostv4, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + ASSERT_TRUE (tls.connecting ()); + } + ASSERT_TRUE (tls.waitConnected (_timeout)) << join::lastError.message (); + if (tls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitDisconnected (_timeout)) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test handshake method. + */ +TEST_F (TlsSocket, handshake) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (tls.handshake (), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (tls.open (Tcp::v4 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test waitHandshake method. + */ +TEST_F (TlsSocket, waitHandshake) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + + ASSERT_EQ (tls.open (Tcp::v6 ()), 0) << join::lastError.message (); + ASSERT_FALSE (tls.waitHandshake (_timeout)); + if (tls.connect ({_hostv6, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitConnected (_timeout)) << join::lastError.message (); + if (tls.handshake () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitHandshake (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tls.waitHandshake (_timeout)) << join::lastError.message (); + if (tls.shutdown () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitShutdown (_timeout)) << join::lastError.message (); + if (tls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitDisconnected (_timeout)) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test disconnect method. + */ +TEST_F (TlsSocket, disconnect) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_FALSE (tls.connected ()); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_TRUE (tls.connected ()); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + ASSERT_FALSE (tls.connected ()); + tls.close (); + ASSERT_FALSE (tls.connected ()); +} + +/** + * @brief Test waitDisconnected method. + */ +TEST_F (TlsSocket, waitDisconnected) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + + ASSERT_TRUE (tls.waitDisconnected (_timeout)) << join::lastError.message (); + if (tls.connect ({_hostv4, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitConnected (_timeout)) << join::lastError.message (); + ASSERT_FALSE (tls.waitDisconnected (_timeout)); + if (tls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitDisconnected (_timeout)) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test waitReadyRead method. + */ +TEST_F (TlsSocket, waitReadyRead) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_FALSE (tls.waitReadyRead (_timeout)); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + if (tls.connect ({_hostv4, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitConnected (_timeout)) << join::lastError.message (); + if (tls.handshake () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitHandshake (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_EQ (tls.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyRead (_timeout)) << join::lastError.message (); + if (tls.shutdown () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitShutdown (_timeout)) << join::lastError.message (); + if (tls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitDisconnected (_timeout)) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test read method. + */ +TEST_F (TlsSocket, read) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_EQ (tls.read (data, sizeof (data)), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_EQ (tls.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_GT (tls.read (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); +} + +TEST_F (TlsSocket, readExactly) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_EQ (tls.readExactly (data, sizeof (data)), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_EQ (tls.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_EQ (tls.readExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test waitReadyWrite method. + */ +TEST_F (TlsSocket, waitReadyWrite) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + + ASSERT_FALSE (tls.waitReadyWrite (_timeout)); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + if (tls.connect ({_hostv4, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitConnected (_timeout)) << join::lastError.message (); + if (tls.handshake () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitHandshake (_timeout)) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); + if (tls.shutdown () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitShutdown (_timeout)) << join::lastError.message (); + if (tls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (tls.waitDisconnected (_timeout)) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test write method. + */ +TEST_F (TlsSocket, write) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_EQ (tls.write (data, sizeof (data)), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_GT (tls.write (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); +} + +TEST_F (TlsSocket, writeExactly) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; + + ASSERT_EQ (tls.writeExactly (data, sizeof (data)), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyWrite (_timeout)) << join::lastError.message (); + ASSERT_EQ (tls.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); + ASSERT_TRUE (tls.waitReadyRead (_timeout)) << join::lastError.message (); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test setMode method. + */ +TEST_F (TlsSocket, setMode) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + + ASSERT_EQ (tls.open (), 0) << join::lastError.message (); + + int flags = ::fcntl (tls.handle (), F_GETFL, 0); + ASSERT_TRUE (flags & O_NONBLOCK); + + tls.setMode (Tcp::Socket::Blocking); + flags = ::fcntl (tls.handle (), F_GETFL, 0); + ASSERT_FALSE (flags & O_NONBLOCK); + + tls.setMode (Tcp::Socket::NonBlocking); + flags = ::fcntl (tls.handle (), F_GETFL, 0); + ASSERT_TRUE (flags & O_NONBLOCK); + + tls.close (); +} + +/** + * @brief Test setOption method. + */ +TEST_F (TlsSocket, setOption) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + + ASSERT_EQ (tls.setOption (Tcp::Socket::RcvBuffer, 1500), -1); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + + ASSERT_EQ (tls.open (), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::NoDelay, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::KeepAlive, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::KeepIdle, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::KeepIntvl, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::KeepCount, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::SndBuffer, 1500), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::RcvBuffer, 1500), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::TimeStamp, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::ReuseAddr, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::ReusePort, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::Broadcast, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::Ttl, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::MulticastLoop, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::MulticastTtl, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (tls.setOption (Tcp::Socket::PathMtuDiscover, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::RcvError, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::AuxData, 1), -1); + ASSERT_EQ (join::lastError, std::errc::no_protocol_option); + tls.close (); + + ASSERT_EQ (tls.open (Tcp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::NoDelay, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::KeepAlive, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::KeepIdle, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::KeepIntvl, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::KeepCount, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::SndBuffer, 1500), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::RcvBuffer, 1500), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::TimeStamp, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::ReuseAddr, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::ReusePort, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::Broadcast, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::Ttl, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::MulticastLoop, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::MulticastTtl, 1), -1); + ASSERT_EQ (join::lastError, Errc::InvalidParam); + ASSERT_EQ (tls.setOption (Tcp::Socket::PathMtuDiscover, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::RcvError, 1), 0) << join::lastError.message (); + ASSERT_EQ (tls.setOption (Tcp::Socket::AuxData, 1), -1); + ASSERT_EQ (join::lastError, std::errc::no_protocol_option); + tls.close (); +} + +/** + * @brief Test localEndpoint method. + */ +TEST_F (TlsSocket, localEndpoint) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (tls.localEndpoint (), Tcp::Endpoint{}); + ASSERT_EQ (tls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.localEndpoint (), Tcp::Endpoint (_hostv4, uint16_t (_port + 1))) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test remoteEndpoint method. + */ +TEST_F (TlsSocket, remoteEndpoint) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (tls.remoteEndpoint (), Tcp::Endpoint{}); + ASSERT_EQ (tls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.remoteEndpoint (), Tcp::Endpoint (_hostv4, _port)) << join::lastError.message (); + tls.close (); +} + +/** + * @brief Test opened method. + */ +TEST_F (TlsSocket, opened) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_FALSE (tls.opened ()); + ASSERT_EQ (tls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_TRUE (tls.opened ()); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (tls.opened ()); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + ASSERT_TRUE (tls.opened ()); + tls.close (); + ASSERT_FALSE (tls.opened ()); +} + +/** + * @brief Test connected method. + */ +TEST_F (TlsSocket, connected) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_FALSE (tls.connected ()); + ASSERT_EQ (tls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_FALSE (tls.connected ()); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (tls.connected ()); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + ASSERT_FALSE (tls.connected ()); + tls.close (); + ASSERT_FALSE (tls.connected ()); +} + +/** + * @brief Test encrypted method. + */ +TEST_F (TlsSocket, encrypted) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_FALSE (tls.encrypted ()); + ASSERT_EQ (tls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_FALSE (tls.encrypted ()); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_FALSE (tls.encrypted ()); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_TRUE (tls.encrypted ()); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_FALSE (tls.encrypted ()); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + ASSERT_FALSE (tls.encrypted ()); + tls.close (); + ASSERT_FALSE (tls.encrypted ()); +} + +/** + * @brief Test family method. + */ +TEST_F (TlsSocket, family) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (tls.family (), AF_INET); + + ASSERT_EQ (tls.bind (IpAddress (AF_INET6)), 0) << join::lastError.message (); + ASSERT_EQ (tls.family (), AF_INET6); + tls.close (); + + ASSERT_EQ (tls.bind (IpAddress (AF_INET)), 0) << join::lastError.message (); + ASSERT_EQ (tls.family (), AF_INET); + tls.close (); +} + +/** + * @brief Test type method. + */ +TEST_F (TlsSocket, type) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + + ASSERT_EQ (tls.type (), SOCK_STREAM); +} + +/** + * @brief Test protocol method. + */ +TEST_F (TlsSocket, protocol) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + + ASSERT_EQ (tls.protocol (), IPPROTO_TCP); +} + +/** + * @brief Test handle method. + */ +TEST_F (TlsSocket, handle) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (tls.handle (), -1); + ASSERT_EQ (tls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_GT (tls.handle (), -1); + ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_GT (tls.handle (), -1); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + ASSERT_GT (tls.handle (), -1); + tls.close (); + ASSERT_EQ (tls.handle (), -1); +} + +/** + * @brief Test mtu method. + */ +TEST_F (TlsSocket, mtu) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls (ctx, Tcp::Socket::Blocking); + + ASSERT_EQ (tls.mtu (), -1); + ASSERT_EQ (tls.connect ({"127.0.0.1", _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_NE (tls.mtu (), -1) << join::lastError.message (); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + ASSERT_NE (tls.mtu (), -1) << join::lastError.message (); + tls.close (); + ASSERT_EQ (tls.mtu (), -1); + + ASSERT_EQ (tls.mtu (), -1); + ASSERT_EQ (tls.connect ({"::1", _port}), 0) << join::lastError.message (); + ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); + ASSERT_NE (tls.mtu (), -1) << join::lastError.message (); + ASSERT_EQ (tls.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); + ASSERT_NE (tls.mtu (), -1) << join::lastError.message (); + tls.close (); + ASSERT_EQ (tls.mtu (), -1); +} + +/** + * @brief Test cert verification. + */ +TEST_F (TlsSocket, verify) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + Tcp::Endpoint endpoint{_hostv4, _port}; + + ctx.setVerify (false); + TlsWrapper tls1 (ctx, Tcp::Socket::Blocking); + ASSERT_EQ (tls1.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (tls1.handshake (), 0) << join::lastError.message (); + ASSERT_EQ (tls1.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls1.disconnect (), 0) << join::lastError.message (); + tls1.close (); + + ctx.setVerify (true, 0); + TlsWrapper tls2 (ctx, Tcp::Socket::Blocking); + ASSERT_EQ (tls2.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (tls2.handshake (), -1); + ASSERT_EQ (tls2.disconnect (), 0) << join::lastError.message (); + tls2.close (); + + ctx.setVerify (true, 1); + TlsWrapper tls3 (ctx, Tcp::Socket::Blocking); + ASSERT_EQ (tls3.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (tls3.handshake (), -1); + ASSERT_EQ (tls3.disconnect (), 0) << join::lastError.message (); + tls3.close (); + + endpoint.hostname ("localhost"); + + ctx.setVerify (true, 0); + TlsWrapper tls4 (ctx, Tcp::Socket::Blocking); + ASSERT_EQ (tls4.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (tls4.handshake (), -1); + ASSERT_EQ (tls4.disconnect (), 0) << join::lastError.message (); + tls4.close (); + + ctx.setVerify (true, 1); + TlsWrapper tls5 (ctx, Tcp::Socket::Blocking); + ASSERT_EQ (tls5.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (tls5.handshake (), -1); + ASSERT_EQ (tls5.disconnect (), 0) << join::lastError.message (); + tls5.close (); + + ASSERT_EQ (ctx.setCaFile (_rootcert), 0) << join::lastError.message (); + + ctx.setVerify (true, 0); + TlsWrapper tls6 (ctx, Tcp::Socket::Blocking); + ASSERT_EQ (tls6.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (tls6.handshake (), -1); + ASSERT_EQ (tls6.disconnect (), 0) << join::lastError.message (); + tls6.close (); + + ctx.setVerify (true, 1); + TlsWrapper tls7 (ctx, Tcp::Socket::Blocking); + ASSERT_EQ (tls7.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (tls7.handshake (), 0) << join::lastError.message (); + ASSERT_EQ (tls7.shutdown (), 0) << join::lastError.message (); + ASSERT_EQ (tls7.disconnect (), 0) << join::lastError.message (); + tls7.close (); +} + +/** + * @brief Test is lower method. + */ +TEST_F (TlsSocket, isLower) +{ + TlsContext ctx (TlsContext::Role::TlsClient); + TlsWrapper tls1 (ctx), tls2 (ctx); + + ASSERT_EQ (tls1.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tls2.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + if (tls1.handle () < tls2.handle ()) + { + ASSERT_TRUE (tls1 < tls2); + } + else + { + ASSERT_TRUE (tls2 < tls1); + } + tls1.close (); + tls2.close (); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + join::initializeOpenSSL (); + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} From 3a8c863678cca1ec159a21c9eb28009323997834 Mon Sep 17 00:00:00 2001 From: mrabine Date: Mon, 1 Jun 2026 23:52:44 +0200 Subject: [PATCH 2/9] fix shutdown --- core/include/join/socket.hpp | 18 +++---- crypto/include/join/tlswrapper.hpp | 33 ++++++++---- crypto/tests/dtlswrapper_test.cpp | 84 ++++++++++++++++-------------- crypto/tests/tlscontext_test.cpp | 8 +-- crypto/tests/tlswrapper_test.cpp | 27 +++++----- 5 files changed, 94 insertions(+), 76 deletions(-) diff --git a/core/include/join/socket.hpp b/core/include/join/socket.hpp index c89e3296..b62c3c5d 100644 --- a/core/include/join/socket.hpp +++ b/core/include/join/socket.hpp @@ -1063,6 +1063,15 @@ namespace join return this->_remote; } + /** + * @brief check if the socket is connecting. + * @return true if connecting, false otherwise. + */ + virtual bool connecting () const noexcept + { + return (this->_state == State::Connecting); + } + /** * @brief check if the socket is connected. * @return true if connected, false otherwise. @@ -1447,15 +1456,6 @@ namespace join return 0; } - /** - * @brief check if the socket is connecting. - * @return true if connecting, false otherwise. - */ - virtual bool connecting () const noexcept - { - return (this->_state == State::Connecting); - } - /** * @brief check if the socket is connected. * @return true if connected, false otherwise. diff --git a/crypto/include/join/tlswrapper.hpp b/crypto/include/join/tlswrapper.hpp index 285c7c69..e8b246e3 100644 --- a/crypto/include/join/tlswrapper.hpp +++ b/crypto/include/join/tlswrapper.hpp @@ -326,7 +326,13 @@ namespace join int result = SSL_do_handshake (_ssl.get ()); if (result < 1) { - return handleTlsError (result); + // return handleTlsError (result); + int ret = handleTlsError (result); + if (lastError != make_error_code (Errc::TemporaryError)) + { + _ssl.reset (); + } + return ret; } return 0; @@ -387,6 +393,10 @@ namespace join lastError = make_error_code (TlsErrc::TlsProtocolError); return false; } + else if (ret == 0) + { + continue; + } lastError = make_error_code (Errc::TemporaryError); if (handshake () == 0) @@ -438,18 +448,19 @@ namespace join } } - if (_socket.type () == SOCK_STREAM) - { - _ssl.reset (); - return 0; - } - - if ((SSL_get_shutdown (_ssl.get ()) & SSL_RECEIVED_SHUTDOWN) == 0) + if (_socket.type () == SOCK_DGRAM) { - int result = SSL_shutdown (_ssl.get ()); - if (result < 0) + if ((SSL_get_shutdown (_ssl.get ()) & SSL_RECEIVED_SHUTDOWN) == 0) { - return handleTlsError (result); + int result = SSL_shutdown (_ssl.get ()); + if (result < 0) + { + handleTlsError (result); + if (lastError != Errc::ConnectionClosed && lastError != TlsErrc::TlsCloseNotifyAlert) + { + return -1; + } + } } } diff --git a/crypto/tests/dtlswrapper_test.cpp b/crypto/tests/dtlswrapper_test.cpp index c8097644..8bb99079 100644 --- a/crypto/tests/dtlswrapper_test.cpp +++ b/crypto/tests/dtlswrapper_test.cpp @@ -213,17 +213,17 @@ class DtlsSocket : public EventHandler, public ::testing::Test */ virtual void onReadable ([[maybe_unused]] int fd) override { - _socket.waitHandshake (_timeout); - - char buffer[65536]; - Udp::Endpoint from; - int nread = _socket.readFrom (buffer, sizeof (buffer), &from); - if (nread > 0) + if (_socket.waitHandshake (_timeout)) { - _socket.writeTo (buffer, nread, from); + char buffer[65536]; + Udp::Endpoint from; + int nread = _socket.readFrom (buffer, sizeof (buffer), &from); + if (nread > 0) + { + _socket.writeTo (buffer, nread, from); + } + _socket.waitShutdown (_timeout); } - - _socket.waitShutdown (_timeout); } /// TLS context serveur. @@ -388,25 +388,24 @@ TEST_F (DtlsSocket, connect) /** * @brief Test waitConnected method. */ -// TEST_F (DtlsSocket, waitConnected) -// { -// TlsContext ctx (TlsContext::Role::DtlsClient); -// TlsWrapper dtls (ctx); - -// ASSERT_FALSE (dtls.waitConnected (_timeout)); -// if (dtls.connect ({_hostv4, _port}) == -1) -// { -// ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); -// ASSERT_TRUE (dtls.connecting ()); -// } -// ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); -// if (dtls.disconnect () == -1) -// { -// ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); -// } -// ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); -// dtls.close (); -// } +TEST_F (DtlsSocket, waitConnected) +{ + TlsContext ctx (TlsContext::Role::DtlsClient); + TlsWrapper dtls (ctx); + + ASSERT_FALSE (dtls.waitConnected (_timeout)); + if (dtls.connect ({_hostv4, _port}) == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); + if (dtls.disconnect () == -1) + { + ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); + } + ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); + dtls.close (); +} /** * @brief Test handshake method. @@ -653,6 +652,7 @@ TEST_F (DtlsSocket, writeExactly) ASSERT_EQ (dtls.writeExactly (data, sizeof (data)), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); + ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); ASSERT_TRUE (dtls.waitReadyWrite (_timeout)) << join::lastError.message (); @@ -920,7 +920,6 @@ TEST_F (DtlsSocket, mtu) ASSERT_NE (dtls.mtu (), -1) << join::lastError.message (); ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); - ASSERT_NE (dtls.mtu (), -1) << join::lastError.message (); dtls.close (); ASSERT_EQ (dtls.mtu (), -1); @@ -930,7 +929,6 @@ TEST_F (DtlsSocket, mtu) ASSERT_NE (dtls.mtu (), -1) << join::lastError.message (); ASSERT_EQ (dtls.shutdown (), 0) << join::lastError.message (); ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); - ASSERT_NE (dtls.mtu (), -1) << join::lastError.message (); dtls.close (); ASSERT_EQ (dtls.mtu (), -1); } @@ -941,11 +939,13 @@ TEST_F (DtlsSocket, mtu) TEST_F (DtlsSocket, verify) { TlsContext ctx (TlsContext::Role::DtlsClient); - Udp::Endpoint endpoint{_hostv4, _port}; + Udp::Endpoint src{_hostv4, _port + 1}; + Udp::Endpoint dest{_hostv4, _port}; ctx.setVerify (false); TlsWrapper dtls1 (ctx, Udp::Socket::Blocking); - ASSERT_EQ (dtls1.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls1.bind (src), 0) << join::lastError.message (); + ASSERT_EQ (dtls1.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls1.handshake (), 0) << join::lastError.message (); ASSERT_EQ (dtls1.shutdown (), 0) << join::lastError.message (); ASSERT_EQ (dtls1.disconnect (), 0) << join::lastError.message (); @@ -953,30 +953,34 @@ TEST_F (DtlsSocket, verify) ctx.setVerify (true, 0); TlsWrapper dtls2 (ctx, Udp::Socket::Blocking); - ASSERT_EQ (dtls2.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls2.bind (src), 0) << join::lastError.message (); + ASSERT_EQ (dtls2.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls2.handshake (), -1); ASSERT_EQ (dtls2.disconnect (), 0) << join::lastError.message (); dtls2.close (); ctx.setVerify (true, 1); TlsWrapper dtls3 (ctx, Udp::Socket::Blocking); - ASSERT_EQ (dtls3.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls3.bind (src), 0) << join::lastError.message (); + ASSERT_EQ (dtls3.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls3.handshake (), -1); ASSERT_EQ (dtls3.disconnect (), 0) << join::lastError.message (); dtls3.close (); - endpoint.hostname ("localhost"); + dest.hostname ("localhost"); ctx.setVerify (true, 0); TlsWrapper dtls4 (ctx, Udp::Socket::Blocking); - ASSERT_EQ (dtls4.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls4.bind (src), 0) << join::lastError.message (); + ASSERT_EQ (dtls4.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls4.handshake (), -1); ASSERT_EQ (dtls4.disconnect (), 0) << join::lastError.message (); dtls4.close (); ctx.setVerify (true, 1); TlsWrapper dtls5 (ctx, Udp::Socket::Blocking); - ASSERT_EQ (dtls5.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls5.bind (src), 0) << join::lastError.message (); + ASSERT_EQ (dtls5.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls5.handshake (), -1); ASSERT_EQ (dtls5.disconnect (), 0) << join::lastError.message (); dtls5.close (); @@ -985,14 +989,16 @@ TEST_F (DtlsSocket, verify) ctx.setVerify (true, 0); TlsWrapper dtls6 (ctx, Udp::Socket::Blocking); - ASSERT_EQ (dtls6.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls6.bind (src), 0) << join::lastError.message (); + ASSERT_EQ (dtls6.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls6.handshake (), -1); ASSERT_EQ (dtls6.disconnect (), 0) << join::lastError.message (); dtls6.close (); ctx.setVerify (true, 1); TlsWrapper dtls7 (ctx, Udp::Socket::Blocking); - ASSERT_EQ (dtls7.connect (endpoint), 0) << join::lastError.message (); + ASSERT_EQ (dtls7.bind (src), 0) << join::lastError.message (); + ASSERT_EQ (dtls7.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls7.handshake (), 0) << join::lastError.message (); ASSERT_EQ (dtls7.shutdown (), 0) << join::lastError.message (); ASSERT_EQ (dtls7.disconnect (), 0) << join::lastError.message (); diff --git a/crypto/tests/tlscontext_test.cpp b/crypto/tests/tlscontext_test.cpp index d0b52cde..fdfe2652 100644 --- a/crypto/tests/tlscontext_test.cpp +++ b/crypto/tests/tlscontext_test.cpp @@ -207,10 +207,10 @@ const std::string TlsContextTest::_invalidKey = "/tmp/tlscontext_test_invalid.ke */ TEST_F (TlsContextTest, construct) { - ASSERT_NO_THROW (TlsContext (TlsContext::Role::TlsClient)); - ASSERT_NO_THROW (TlsContext (TlsContext::Role::TlsServer)); - ASSERT_NO_THROW (TlsContext (TlsContext::Role::DtlsClient)); - ASSERT_NO_THROW (TlsContext (TlsContext::Role::DtlsServer)); + ASSERT_NO_THROW (TlsContext{TlsContext::Role::TlsClient}); + ASSERT_NO_THROW (TlsContext{TlsContext::Role::TlsServer}); + ASSERT_NO_THROW (TlsContext{TlsContext::Role::DtlsClient}); + ASSERT_NO_THROW (TlsContext{TlsContext::Role::DtlsServer}); } /** diff --git a/crypto/tests/tlswrapper_test.cpp b/crypto/tests/tlswrapper_test.cpp index fedd7228..ae86b2e7 100644 --- a/crypto/tests/tlswrapper_test.cpp +++ b/crypto/tests/tlswrapper_test.cpp @@ -217,25 +217,26 @@ class TlsSocket : public EventHandler, public ::testing::Test if (tcp.connected ()) { TlsWrapper tls (std::move (tcp), _tlsContext); - tls.waitHandshake (_timeout); - - char buf[1024]; - for (;;) + if (tls.waitHandshake (_timeout)) { - int nread = tls.read (buf, sizeof (buf)); - if (nread == -1) + char buf[1024]; + for (;;) { - if (join::lastError == Errc::TemporaryError) + int nread = tls.read (buf, sizeof (buf)); + if (nread == -1) { - if (tls.waitReadyRead (_timeout)) - continue; + if (join::lastError == Errc::TemporaryError) + { + if (tls.waitReadyRead (_timeout)) + continue; + } + break; } - break; + tls.writeExactly (buf, nread); } - tls.writeExactly (buf, nread); - } - tls.waitShutdown (_timeout); + tls.waitShutdown (_timeout); + } tls.waitDisconnected (_timeout); tls.close (); } From d76965461b6ed9c70161b4dea9e08742fc9954b0 Mon Sep 17 00:00:00 2001 From: mrabine Date: Tue, 2 Jun 2026 00:22:08 +0200 Subject: [PATCH 3/9] fix tcp tests --- core/include/join/socket.hpp | 2 -- core/tests/tcpsocket_test.cpp | 18 +++++++++--------- core/tests/tcpsocketstream_test.cpp | 4 +++- core/tests/tlssocket_test.cpp | 18 +++++++++--------- core/tests/unixstreamsocket_test.cpp | 6 +++--- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/core/include/join/socket.hpp b/core/include/join/socket.hpp index b62c3c5d..38845378 100644 --- a/core/include/join/socket.hpp +++ b/core/include/join/socket.hpp @@ -1280,8 +1280,6 @@ namespace join this->_state = State::Disconnected; } - // this->close (); - return 0; } diff --git a/core/tests/tcpsocket_test.cpp b/core/tests/tcpsocket_test.cpp index 8d362d1a..b3c541ce 100644 --- a/core/tests/tcpsocket_test.cpp +++ b/core/tests/tcpsocket_test.cpp @@ -147,7 +147,7 @@ TEST_F (TcpSocket, close) ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_TRUE (tcpSocket.opened ()); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (tcpSocket.opened ()); + ASSERT_TRUE (tcpSocket.opened ()); tcpSocket.close (); ASSERT_FALSE (tcpSocket.opened ()); } @@ -162,11 +162,11 @@ TEST_F (TcpSocket, bind) ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tcpSocket.bind (_hostv4), -1); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); + tcpSocket.close (); ASSERT_EQ (tcpSocket.bind (_hostv4), 0) << join::lastError.message (); ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); - tcpSocket.close (); } @@ -178,18 +178,16 @@ TEST_F (TcpSocket, bindToDevice) Tcp::Socket tcpSocket (Tcp::Socket::Blocking); ASSERT_EQ (tcpSocket.bindToDevice ("lo"), -1); - ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tcpSocket.bindToDevice ("lo"), -1); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); + tcpSocket.close (); ASSERT_EQ (tcpSocket.open (Tcp::v6 ()), 0) << join::lastError.message (); ASSERT_EQ (tcpSocket.bindToDevice ("lo"), 0) << join::lastError.message (); ASSERT_EQ (tcpSocket.connect ({_hostv6, _port}), 0) << join::lastError.message (); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tcpSocket.bindToDevice ("foo"), -1); - tcpSocket.close (); } @@ -529,7 +527,7 @@ TEST_F (TcpSocket, opened) ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_TRUE (tcpSocket.opened ()); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (tcpSocket.opened ()); + ASSERT_TRUE (tcpSocket.opened ()); tcpSocket.close (); ASSERT_FALSE (tcpSocket.opened ()); } @@ -621,7 +619,7 @@ TEST_F (TcpSocket, handle) ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_GT (tcpSocket.handle (), -1); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tcpSocket.handle (), -1); + ASSERT_GT (tcpSocket.handle (), -1); tcpSocket.close (); ASSERT_EQ (tcpSocket.handle (), -1); } @@ -637,15 +635,17 @@ TEST_F (TcpSocket, mtu) ASSERT_EQ (tcpSocket.connect ({"127.0.0.1", _port}), 0) << join::lastError.message (); ASSERT_NE (tcpSocket.mtu (), -1) << join::lastError.message (); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tcpSocket.mtu (), -1); + ASSERT_NE (tcpSocket.mtu (), -1) << join::lastError.message (); tcpSocket.close (); + ASSERT_EQ (tcpSocket.mtu (), -1); ASSERT_EQ (tcpSocket.mtu (), -1); ASSERT_EQ (tcpSocket.connect ({"::1", _port}), 0) << join::lastError.message (); ASSERT_NE (tcpSocket.mtu (), -1) << join::lastError.message (); ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tcpSocket.mtu (), -1); + ASSERT_NE (tcpSocket.mtu (), -1) << join::lastError.message (); tcpSocket.close (); + ASSERT_EQ (tcpSocket.mtu (), -1); } /** diff --git a/core/tests/tcpsocketstream_test.cpp b/core/tests/tcpsocketstream_test.cpp index c11c6111..acccfdfa 100644 --- a/core/tests/tcpsocketstream_test.cpp +++ b/core/tests/tcpsocketstream_test.cpp @@ -149,6 +149,8 @@ TEST_F (TcpSocketStream, bind) tcpStream.clear (); tcpStream.disconnect (); ASSERT_TRUE (tcpStream.good ()) << join::lastError.message (); + tcpStream.close (); + ASSERT_TRUE (tcpStream.good ()) << join::lastError.message (); tcpStream.bind (_host); ASSERT_TRUE (tcpStream.good ()) << join::lastError.message (); tcpStream.connect ({_host, _port}); @@ -245,7 +247,7 @@ TEST_F (TcpSocketStream, opened) ASSERT_TRUE (tcpStream.opened ()); tcpStream.disconnect (); ASSERT_TRUE (tcpStream.good ()) << join::lastError.message (); - ASSERT_FALSE (tcpStream.opened ()); + ASSERT_TRUE (tcpStream.opened ()); tcpStream.close (); ASSERT_FALSE (tcpStream.opened ()); } diff --git a/core/tests/tlssocket_test.cpp b/core/tests/tlssocket_test.cpp index 8564bc16..9b329d18 100644 --- a/core/tests/tlssocket_test.cpp +++ b/core/tests/tlssocket_test.cpp @@ -321,7 +321,7 @@ TEST_F (TlsSocket, close) ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_TRUE (tlsSocket.opened ()); ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.opened ()); + ASSERT_TRUE (tlsSocket.opened ()); tlsSocket.close (); ASSERT_FALSE (tlsSocket.opened ()); } @@ -336,11 +336,11 @@ TEST_F (TlsSocket, bind) ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tlsSocket.bind (_hostv4), -1); ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); + tlsSocket.close (); ASSERT_EQ (tlsSocket.bind (_hostv4), 0) << join::lastError.message (); ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); } @@ -352,18 +352,16 @@ TEST_F (TlsSocket, bindToDevice) Tls::Socket tlsSocket (Tls::Socket::Blocking); ASSERT_EQ (tlsSocket.bindToDevice ("lo"), -1); - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tlsSocket.bindToDevice ("lo"), -1); ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); + tlsSocket.close (); ASSERT_EQ (tlsSocket.open (Tls::v6 ()), 0) << join::lastError.message (); ASSERT_EQ (tlsSocket.bindToDevice ("lo"), 0) << join::lastError.message (); ASSERT_EQ (tlsSocket.connect ({_hostv6, _port}), 0) << join::lastError.message (); ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.bindToDevice ("foo"), -1); - tlsSocket.close (); } @@ -776,7 +774,7 @@ TEST_F (TlsSocket, opened) ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_TRUE (tlsSocket.opened ()); ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.opened ()); + ASSERT_TRUE (tlsSocket.opened ()); tlsSocket.close (); ASSERT_FALSE (tlsSocket.opened ()); } @@ -870,7 +868,7 @@ TEST_F (TlsSocket, handle) ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_GT (tlsSocket.handle (), -1); ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.handle (), -1); + ASSERT_GT (tlsSocket.handle (), -1); tlsSocket.close (); ASSERT_EQ (tlsSocket.handle (), -1); } @@ -886,15 +884,17 @@ TEST_F (TlsSocket, mtu) ASSERT_EQ (tlsSocket.connectEncrypted ({"127.0.0.1", _port}), 0) << join::lastError.message (); ASSERT_NE (tlsSocket.mtu (), -1) << join::lastError.message (); ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.mtu (), -1); + ASSERT_NE (tlsSocket.mtu (), -1) << join::lastError.message (); tlsSocket.close (); + ASSERT_EQ (tlsSocket.mtu (), -1); ASSERT_EQ (tlsSocket.mtu (), -1); ASSERT_EQ (tlsSocket.connectEncrypted ({"::1", _port}), 0) << join::lastError.message (); ASSERT_NE (tlsSocket.mtu (), -1) << join::lastError.message (); ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.mtu (), -1); + ASSERT_NE (tlsSocket.mtu (), -1) << join::lastError.message (); tlsSocket.close (); + ASSERT_EQ (tlsSocket.mtu (), -1); } /** diff --git a/core/tests/unixstreamsocket_test.cpp b/core/tests/unixstreamsocket_test.cpp index 0e33fad0..f5fc668f 100644 --- a/core/tests/unixstreamsocket_test.cpp +++ b/core/tests/unixstreamsocket_test.cpp @@ -123,7 +123,7 @@ TEST_F (UnixStreamSocket, close) ASSERT_EQ (unixSocket.connect (_serverpath), 0) << join::lastError.message (); ASSERT_TRUE (unixSocket.opened ()); ASSERT_EQ (unixSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (unixSocket.opened ()); + ASSERT_TRUE (unixSocket.opened ()); unixSocket.close (); ASSERT_FALSE (unixSocket.opened ()); } @@ -479,7 +479,7 @@ TEST_F (UnixStreamSocket, opened) ASSERT_EQ (unixSocket.connect (_serverpath), 0) << join::lastError.message (); ASSERT_TRUE (unixSocket.opened ()); ASSERT_EQ (unixSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (unixSocket.opened ()); + ASSERT_TRUE (unixSocket.opened ()); unixSocket.close (); ASSERT_FALSE (unixSocket.opened ()); } @@ -563,7 +563,7 @@ TEST_F (UnixStreamSocket, handle) ASSERT_EQ (unixSocket.connect (_serverpath), 0) << join::lastError.message (); ASSERT_GT (unixSocket.handle (), -1); ASSERT_EQ (unixSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (unixSocket.handle (), -1); + ASSERT_GT (unixSocket.handle (), -1); unixSocket.close (); ASSERT_EQ (unixSocket.handle (), -1); } From 62bc7e79d9f336f25a49b177e72f8c0bd3a58715 Mon Sep 17 00:00:00 2001 From: mrabine Date: Thu, 4 Jun 2026 22:42:03 +0200 Subject: [PATCH 4/9] refactor code org --- CMakeLists.txt | 1 - core/CMakeLists.txt | 5 - core/include/join/acceptor.hpp | 367 ------ core/include/join/endpoint.hpp | 235 ---- core/include/join/protocol.hpp | 911 +------------- core/include/join/socket.hpp | 1074 ---------------- core/include/join/socketstream.hpp | 159 --- core/tests/CMakeLists.txt | 30 - core/tests/endpoint_test.cpp | 83 -- core/tests/protocol_test.cpp | 99 -- core/tests/tlsacceptor_test.cpp | 486 -------- core/tests/tlserror_test.cpp | 150 --- core/tests/tlssocket_test.cpp | 1085 ----------------- core/tests/tlssocketstream_test.cpp | 934 -------------- crypto/CMakeLists.txt | 12 +- {core => crypto}/include/join/openssl.hpp | 4 +- crypto/include/join/tlswrapper.hpp | 9 +- {core => crypto}/src/openssl.cpp | 0 crypto/tests/CMakeLists.txt | 5 + {core => crypto}/tests/openssl_test.cpp | 0 fabric/CMakeLists.txt | 6 + fabric/include/join/dns.hpp | 140 +++ fabric/include/join/dot.hpp | 136 +++ fabric/include/join/mdns.hpp | 146 +++ fabric/include/join/nameserver.hpp | 63 +- fabric/include/join/netlink.hpp | 127 ++ fabric/include/join/netlink_endpoint.hpp | 271 ++++ fabric/include/join/netlinkmanager.hpp | 4 +- fabric/include/join/resolver.hpp | 521 ++++---- fabric/tests/CMakeLists.txt | 62 +- ...smessage_test.cpp => dns_message_test.cpp} | 0 fabric/tests/dns_protocol_test.cpp | 81 ++ fabric/tests/dns_test.cpp | 1 - fabric/tests/dot_protocol_test.cpp | 81 ++ fabric/tests/dot_test.cpp | 60 +- ...er_test.cpp => interface_manager_test.cpp} | 0 fabric/tests/mdns_protocol_test.cpp | 90 ++ ...ger_test.cpp => neighbor_manager_test.cpp} | 0 fabric/tests/netlink_endpoint_test.cpp | 104 ++ fabric/tests/netlink_protocol_test.cpp | 81 ++ .../tests/netlink_socket_test.cpp | 2 +- ...anager_test.cpp => route_manager_test.cpp} | 0 42 files changed, 1654 insertions(+), 5971 deletions(-) delete mode 100644 core/tests/tlsacceptor_test.cpp delete mode 100644 core/tests/tlserror_test.cpp delete mode 100644 core/tests/tlssocket_test.cpp delete mode 100644 core/tests/tlssocketstream_test.cpp rename {core => crypto}/include/join/openssl.hpp (99%) rename {core => crypto}/src/openssl.cpp (100%) rename {core => crypto}/tests/openssl_test.cpp (100%) create mode 100644 fabric/include/join/dns.hpp create mode 100644 fabric/include/join/dot.hpp create mode 100644 fabric/include/join/mdns.hpp create mode 100644 fabric/include/join/netlink.hpp create mode 100644 fabric/include/join/netlink_endpoint.hpp rename fabric/tests/{dnsmessage_test.cpp => dns_message_test.cpp} (100%) create mode 100644 fabric/tests/dns_protocol_test.cpp create mode 100644 fabric/tests/dot_protocol_test.cpp rename fabric/tests/{interfacemanager_test.cpp => interface_manager_test.cpp} (100%) create mode 100644 fabric/tests/mdns_protocol_test.cpp rename fabric/tests/{neighbormanager_test.cpp => neighbor_manager_test.cpp} (100%) create mode 100644 fabric/tests/netlink_endpoint_test.cpp create mode 100644 fabric/tests/netlink_protocol_test.cpp rename core/tests/netlinksocket_test.cpp => fabric/tests/netlink_socket_test.cpp (99%) rename fabric/tests/{routemanager_test.cpp => route_manager_test.cpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5f34c43e..cb12acfb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,6 @@ endif() find_package(PkgConfig REQUIRED) find_package(Threads REQUIRED) -find_package(OpenSSL REQUIRED) add_subdirectory(core) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 5809a341..23ce6486 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -36,7 +36,6 @@ set(PUBLIC_HEADERS include/join/socket.hpp include/join/socketstream.hpp include/join/acceptor.hpp - include/join/openssl.hpp include/join/cpu.hpp include/join/clock.hpp include/join/timer.hpp @@ -58,10 +57,8 @@ set(SOURCES src/semaphore.cpp src/thread.cpp src/threadpool.cpp - src/openssl.cpp src/macaddress.cpp src/ipaddress.cpp - src/socket.cpp src/cpu.cpp ) @@ -86,8 +83,6 @@ target_include_directories(${JOIN_CORE} target_link_libraries(${JOIN_CORE} PUBLIC - OpenSSL::SSL - OpenSSL::Crypto Threads::Threads $<$:${LIBURING_LIBRARIES}> $<$:${NUMA_LIBRARIES}> diff --git a/core/include/join/acceptor.hpp b/core/include/join/acceptor.hpp index 3a253e17..5f538f8b 100644 --- a/core/include/join/acceptor.hpp +++ b/core/include/join/acceptor.hpp @@ -284,373 +284,6 @@ namespace join /// protocol. Protocol _protocol; }; - - /** - * @brief basic TLS acceptor class. - */ - template - class BasicTlsAcceptor : public BasicStreamAcceptor - { - public: - using Endpoint = typename Protocol::Endpoint; - using Socket = typename Protocol::Socket; - using Stream = typename Protocol::Stream; - - /** - * @brief create the acceptor instance. - */ - BasicTlsAcceptor () - : BasicStreamAcceptor () - , _tlsContext (SSL_CTX_new (TLS_server_method ()), SslCtxDelete ()) - , _sessionId (randomize ()) - { - // enable the OpenSSL bug workaround options. - SSL_CTX_set_options (_tlsContext.get (), SSL_OP_ALL); - - // disallow compression. - SSL_CTX_set_options (_tlsContext.get (), SSL_OP_NO_COMPRESSION); - - // disallow usage of SSLv2, SSLv3, TLSv1 and TLSv1.1 which are considered insecure. - SSL_CTX_set_options (_tlsContext.get (), - SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); - - // choose the cipher according to the server's preferences. - SSL_CTX_set_options (_tlsContext.get (), SSL_OP_CIPHER_SERVER_PREFERENCE); - - // setup write mode. - SSL_CTX_set_mode (_tlsContext.get (), SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - // automatically renegotiates. - SSL_CTX_set_mode (_tlsContext.get (), SSL_MODE_AUTO_RETRY); - - // enable SSL session caching. - SSL_CTX_set_session_id_context (_tlsContext.get (), reinterpret_cast (&_sessionId), - sizeof (_sessionId)); - - // no verification by default. - SSL_CTX_set_verify (_tlsContext.get (), SSL_VERIFY_NONE, nullptr); - - // set default TLSv1.2 and below cipher suites. - SSL_CTX_set_cipher_list (_tlsContext.get (), join::defaultCipher.c_str ()); - - // set default TLSv1.3 cipher suites. - SSL_CTX_set_ciphersuites (_tlsContext.get (), join::defaultCipher_1_3.c_str ()); - - // disallow client-side renegotiation. - SSL_CTX_set_options (_tlsContext.get (), SSL_OP_NO_RENEGOTIATION); - -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - // use the default built-in Diffie-Hellman parameters. - SSL_CTX_set_dh_auto (_tlsContext.get (), 1); - - // Set elliptic curve Diffie-Hellman key. - SSL_CTX_set1_groups_list (_tlsContext.get (), join::defaultCurve.c_str ()); -#else - // Set Diffie-Hellman key. - join::DhKeyPtr dh (getDh2236 ()); - if (dh) - { - SSL_CTX_set_tmp_dh (_tlsContext.get (), dh.get ()); - } - - // Set elliptic curve Diffie-Hellman key. - join::EcdhKeyPtr ecdh (EC_KEY_new_by_curve_name (NID_X9_62_prime256v1)); - if (ecdh) - { - SSL_CTX_set_tmp_ecdh (_tlsContext.get (), ecdh.get ()); - } -#endif - } - - /** - * @brief copy constructor. - * @param other other object to copy. - */ - BasicTlsAcceptor (const BasicTlsAcceptor& other) = delete; - - /** - * @brief copy assignment operator. - * @param other other object to assign. - * @return assigned object. - */ - BasicTlsAcceptor& operator= (const BasicTlsAcceptor& other) = delete; - - /** - * @brief move constructor. - * @param other other object to move. - */ - BasicTlsAcceptor (BasicTlsAcceptor&& other) - : BasicStreamAcceptor (std::move (other)) - , _tlsContext (std::move (other._tlsContext)) - , _sessionId (other._sessionId) - { - other._sessionId = 0; - } - - /** - * @brief move assignment operator. - * @param other other object to assign. - * @return assigned object. - */ - BasicTlsAcceptor& operator= (BasicTlsAcceptor&& other) - { - BasicStreamAcceptor::operator= (std::move (other)); - _tlsContext = std::move (other._tlsContext); - _sessionId = other._sessionId; - other._sessionId = 0; - return *this; - } - - /** - * @brief accept new connection and fill in the client object with connection parameters. - * @return the accepted client socket object. - */ - virtual Socket accept () const override - { - struct sockaddr_storage sa; - socklen_t sa_len = sizeof (struct sockaddr_storage); - Socket sock (this->_tlsContext); - - sock._handle = ::accept4 (this->_handle, reinterpret_cast (&sa), &sa_len, - SOCK_NONBLOCK | SOCK_CLOEXEC); - if (sock._handle == -1) - { - lastError = std::error_code (errno, std::generic_category ()); - return sock; - } - - sock._remote = Endpoint (reinterpret_cast (&sa), sa_len); - sock._state = Socket::Connected; - - sock.setOption (Socket::NoDelay, 1); - - return sock; - } - - /** - * @brief accept new connection and fill in the client object with connection parameters. - * @return the accepted client socket object. - */ - virtual Socket acceptEncrypted () const - { - Socket sock = this->accept (); - if (!sock.connected ()) - { - return sock; - } - - sock._tlsHandle.reset (SSL_new (sock._tlsContext.get ())); - if (sock._tlsHandle == nullptr) - { - lastError = make_error_code (Errc::OutOfMemory); - sock.close (); - return sock; - } - - SSL_set_fd (sock._tlsHandle.get (), sock._handle); - SSL_set_accept_state (sock._tlsHandle.get ()); - SSL_set_app_data (sock._tlsHandle.get (), &sock); -#ifdef DEBUG - SSL_set_info_callback (sock._tlsHandle.get (), Socket::infoWrapper); -#endif - - sock._tlsState = Socket::Encrypted; - - return sock; - } - - /** - * @brief accept new connection and fill in the client object with connection parameters. - * @return The client stream object on success, nullptr on failure. - */ - virtual Stream acceptStreamEncrypted () const - { - Stream stream; - stream.socket () = this->acceptEncrypted (); - return stream; - } - - /** - * @brief set the certificate and the private key. - * @param cert certificate path. - * @param key private key path. - * @return 0 on success, -1 on failure. - */ - int setCertificate (const std::string& cert, const std::string& key = "") - { - if (SSL_CTX_use_certificate_file (this->_tlsContext.get (), cert.c_str (), SSL_FILETYPE_PEM) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - if (key.size ()) - { - if (SSL_CTX_use_PrivateKey_file (this->_tlsContext.get (), key.c_str (), SSL_FILETYPE_PEM) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - } - - // check the consistency of the private key and the certificate. - if (SSL_CTX_check_private_key (this->_tlsContext.get ()) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - this->close (); - return -1; - } - - return 0; - } - - /** - * @brief Set the location of the trusted CA certificate. - * @param caFile path of the trusted CA certificate file. - * @return 0 on success, -1 on failure. - */ - int setCaCertificate (const std::string& caFile) - { - join::StackOfX509NamePtr certNames (SSL_load_client_CA_file (caFile.c_str ())); - if (certNames == nullptr) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - SSL_CTX_load_verify_locations (this->_tlsContext.get (), caFile.c_str (), nullptr); - SSL_CTX_set_client_CA_list (this->_tlsContext.get (), certNames.release ()); - - return 0; - } - - /** - * @brief Enable/Disable the verification of the peer certificate. - * @param verify Enable peer verification if set to true, false otherwise. - * @param depth The maximum certificate verification depth (default: no limit). - */ - void setVerify (bool verify, int depth = -1) - { - if (verify) - { - // SSL_VERIFY_PEER will lead the client to verify the server certificate. - // If the verification process fails, the TLS/SSL handshake is immediately terminated. - SSL_CTX_set_verify (this->_tlsContext.get (), SSL_VERIFY_PEER, Socket::verifyWrapper); - SSL_CTX_set_verify_depth (this->_tlsContext.get (), depth); - } - else - { - // SSL_VERIFY_NONE will lead the client to continue the handshake regardless of the verification result. - SSL_CTX_set_verify (this->_tlsContext.get (), SSL_VERIFY_NONE, nullptr); - } - } - - /** - * @brief set the cipher list (TLSv1.2 and below). - * @param cipher the cipher list. - * @return 0 on success, -1 on failure. - */ - int setCipher (const std::string& cipher) - { - if (SSL_CTX_set_cipher_list (this->_tlsContext.get (), cipher.c_str ()) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - return 0; - } - - /** - * @brief set the cipher list (TLSv1.3). - * @param cipher the cipher list. - * @return 0 on success, -1 on failure. - */ - int setCipher_1_3 (const std::string& cipher) - { - if (SSL_CTX_set_ciphersuites (this->_tlsContext.get (), cipher.c_str ()) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - return 0; - } - -#if OPENSSL_VERSION_NUMBER >= 0x30000000L - /** - * @brief set curve list. - * @param curves curve list. - * @return 0 on success, -1 on failure. - */ - int setCurve (const std::string& curves) - { - if (SSL_CTX_set1_groups_list (this->_tlsContext.get (), curves.c_str ()) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - return 0; - } -#else - - protected: - /** - * @brief generate openssl Diffie-Hellman parameters. - * @note random Diffie-Hellman parameters generated using the command "openssl dhparam -C 2236". - * @return Diffie-Hellman parameters. - */ - static DH* getDh2236 () - { - static unsigned char dhp_2236[] = { - 0x0C, 0xA5, 0x51, 0x2B, 0x8F, 0xF7, 0xA8, 0x74, 0x4D, 0x52, 0xD7, 0xED, 0x97, 0x83, 0xA4, 0xD2, 0x8B, - 0xF3, 0xE7, 0x92, 0xF0, 0x27, 0x1B, 0xA0, 0x80, 0x83, 0x19, 0xDD, 0x02, 0xEF, 0xA3, 0xE6, 0x13, 0x0A, - 0x47, 0xE6, 0xF1, 0x3B, 0xC1, 0x5F, 0x63, 0xC4, 0x03, 0xBA, 0xAC, 0xAA, 0xA3, 0x44, 0xC2, 0x03, 0x6D, - 0x62, 0x33, 0xAA, 0xF9, 0xA2, 0x5A, 0x98, 0xC2, 0xC0, 0x71, 0x6F, 0xB0, 0x93, 0x6A, 0x26, 0x92, 0x90, - 0x95, 0xEA, 0xE8, 0x5F, 0x81, 0x50, 0x57, 0xB3, 0xB7, 0xE6, 0x3A, 0x3A, 0x90, 0x15, 0x01, 0x2F, 0xC7, - 0x8F, 0xAA, 0x0C, 0xAE, 0xC0, 0xFF, 0x3A, 0xA7, 0x26, 0x5C, 0x87, 0xC2, 0x00, 0x68, 0xCA, 0x02, 0x06, - 0x50, 0x44, 0xEE, 0x75, 0xE7, 0xFF, 0x16, 0xD1, 0x0F, 0x64, 0x51, 0x97, 0x52, 0x54, 0x69, 0xF0, 0x31, - 0x81, 0x4D, 0xEB, 0xF5, 0xA8, 0xB3, 0x7B, 0x48, 0x60, 0xBD, 0xC7, 0xC9, 0x6E, 0x97, 0x86, 0x9B, 0xE6, - 0x66, 0x4E, 0x1D, 0xE5, 0x6F, 0xBA, 0xC5, 0x3D, 0xFD, 0x3F, 0x34, 0x69, 0x6F, 0xC0, 0xFA, 0x8D, 0x42, - 0x73, 0xA2, 0x49, 0xDE, 0xB6, 0x8D, 0x71, 0x15, 0xFC, 0xB4, 0x18, 0x31, 0x5A, 0x24, 0xD0, 0x5E, 0xA8, - 0xE0, 0xD8, 0x1C, 0xF8, 0x0F, 0x1F, 0x59, 0x22, 0x5A, 0x07, 0x75, 0x06, 0x98, 0x58, 0xE1, 0xF6, 0xA5, - 0x53, 0xFD, 0x66, 0x1E, 0x8F, 0x41, 0x63, 0x61, 0xA1, 0x79, 0x0D, 0x3B, 0xA7, 0xF4, 0xBD, 0x72, 0xEB, - 0xE1, 0xDC, 0xE2, 0xC9, 0x9B, 0x41, 0xF6, 0x33, 0x3F, 0x9F, 0x0C, 0x33, 0x7B, 0xF2, 0x90, 0x68, 0x28, - 0xD3, 0x5A, 0xC1, 0x5C, 0xDE, 0x15, 0x11, 0xF4, 0xDD, 0xCB, 0x09, 0x78, 0x63, 0x3B, 0xB6, 0xE8, 0xEE, - 0x9A, 0x48, 0xE9, 0x79, 0x80, 0x3F, 0x34, 0x8D, 0xB9, 0x24, 0x8D, 0x94, 0x88, 0xA9, 0x75, 0xA5, 0x19, - 0x05, 0x8D, 0x77, 0x20, 0xAF, 0xC2, 0xC9, 0x7B, 0xD2, 0x51, 0xEE, 0x17, 0x22, 0xAC, 0x33, 0xA8, 0xA6, - 0x1B, 0x8B, 0xE3, 0x79, 0xF3, 0xE8, 0x3B, 0x6B}; - - static unsigned char dhg_2236[] = {0x02}; - - DH* dh = DH_new (); - if (dh == nullptr) - { - return nullptr; - } - - BIGNUM* p = BN_bin2bn (dhp_2236, sizeof (dhp_2236), nullptr); - BIGNUM* g = BN_bin2bn (dhg_2236, sizeof (dhg_2236), nullptr); - if (p == nullptr || g == nullptr || !DH_set0_pqg (dh, p, nullptr, g)) - { - DH_free (dh); - BN_free (p); - BN_free (g); - return nullptr; - } - - return dh; - } -#endif - - protected: - /// SSL/TLS context. - join::SslCtxPtr _tlsContext; - - /// SSL session id. - int _sessionId = 0; - }; } #endif diff --git a/core/include/join/endpoint.hpp b/core/include/join/endpoint.hpp index a1ec582f..38844215 100644 --- a/core/include/join/endpoint.hpp +++ b/core/include/join/endpoint.hpp @@ -33,10 +33,7 @@ #include // C. -#include #include -#include -#include #include #include @@ -258,238 +255,6 @@ namespace join return os; } - /** - * @brief basic netlink endpoint class. - */ - template - class BasicNetlinkEndpoint : public BasicEndpoint - { - public: - /** - * @brief default constructor. - */ - constexpr BasicNetlinkEndpoint () noexcept - : BasicEndpoint () - , _protocol (Protocol ().protocol ()) - { - } - - /** - * @brief create instance using socket address. - * @param addr socket address. - * @param len socket address length. - */ - BasicNetlinkEndpoint (const struct sockaddr* addr, socklen_t len) noexcept - : BasicEndpoint (addr, len) - , _protocol (Protocol ().protocol ()) - { - } - - /** - * @brief create instance using netlink groups. - * @param protocol netlink protocol. - * @param pid process id. - * @param groups netlink groups to set. - */ - BasicNetlinkEndpoint (const Protocol& protocol, uint32_t pid, uint32_t groups) noexcept - : BasicEndpoint () - , _protocol (protocol.protocol ()) - { - struct sockaddr_nl* nl = reinterpret_cast (&this->_addr); - nl->nl_pid = pid; - nl->nl_groups = groups; - } - - /** - * @brief create instance using netlink groups. - * @param pid process id. - * @param groups netlink groups to set. - */ - BasicNetlinkEndpoint (uint32_t pid, uint32_t groups) noexcept - : BasicNetlinkEndpoint (Protocol (), pid, groups) - { - } - - /** - * @brief create instance using netlink groups. - * @param protocol netlink protocol. - * @param groups netlink groups to set. - */ - BasicNetlinkEndpoint (const Protocol& protocol, uint32_t groups) noexcept - : BasicNetlinkEndpoint (protocol, getpid (), groups) - { - } - - /** - * @brief create instance using netlink groups. - * @param groups netlink groups to set. - */ - BasicNetlinkEndpoint (uint32_t groups) noexcept - : BasicNetlinkEndpoint (Protocol (), getpid (), groups) - { - } - - /** - * @brief get endpoint protocol. - * @return endpoint protocol. - */ - Protocol protocol () const noexcept - { - if (_protocol == NETLINK_NETFILTER) - { - return Protocol::nf (); - } - return Protocol::rt (); - } - - /** - * @brief get socket address length. - * @return socket address length. - */ - constexpr socklen_t length () const noexcept - { - return sizeof (struct sockaddr_nl); - } - - /** - * @brief set process id. - * @param pid process id. - */ - void pid (uint32_t pid) noexcept - { - reinterpret_cast (&this->_addr)->nl_pid = pid; - } - - /** - * @brief get process id. - * @return process id. - */ - uint32_t pid () const noexcept - { - return reinterpret_cast (&this->_addr)->nl_pid; - } - - /** - * @brief set netlink groups. - * @param groups netlink groups bitmask. - */ - void groups (uint32_t groups) noexcept - { - reinterpret_cast (&this->_addr)->nl_groups = groups; - } - - /** - * @brief get netlink groups. - * @return netlink groups bitmask. - */ - uint32_t groups () const noexcept - { - return reinterpret_cast (&this->_addr)->nl_groups; - } - - /** - * @brief get device name (not applicable for netlink). - * @return empty string. - */ - std::string device () const - { - return std::string (); - } - - protected: - /// netlink protocol type. - int _protocol; - }; - - /** - * @brief compare if endpoints are equal. - * @param a endpoint to compare. - * @param b endpoint to compare to. - * @return true if endpoints are equal, false otherwise. - */ - template - bool operator== (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept - { - return a.pid () == b.pid () && a.groups () == b.groups (); - } - - /** - * @brief compare if endpoints are not equal. - * @param a endpoint to compare. - * @param b endpoint to compare to. - * @return true if endpoints are not equal, false otherwise. - */ - template - bool operator!= (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept - { - return !(a == b); - } - - /** - * @brief compare if endpoint is lower. - * @param a endpoint to compare. - * @param b endpoint to compare to. - * @return true if lower, false otherwise. - */ - template - bool operator< (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept - { - if (a.pid () != b.pid ()) - { - return a.pid () < b.pid (); - } - return a.groups () < b.groups (); - } - - /** - * @brief compare if endpoint is greater. - * @param a endpoint to compare. - * @param b endpoint to compare to. - * @return true if greater, false otherwise. - */ - template - bool operator> (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept - { - return b < a; - } - - /** - * @brief compare if endpoint is lower or equal. - * @param a endpoint to compare. - * @param b endpoint to compare to. - * @return true if lower or equal, false otherwise. - */ - template - bool operator<= (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept - { - return !(b < a); - } - - /** - * @brief compare if endpoint is greater or equal. - * @param a endpoint to compare. - * @param b endpoint to compare to. - * @return true if greater or equal, false otherwise. - */ - template - bool operator>= (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept - { - return !(a < b); - } - - /** - * @brief push endpoint representation into a stream. - * @param os output stream. - * @param endpoint endpoint to push. - * @return output stream. - */ - template - std::ostream& operator<< (std::ostream& os, const BasicNetlinkEndpoint& endpoint) - { - os << "pid=" << endpoint.pid () << ",groups=" << endpoint.groups (); - return os; - } - /** * @brief basic link layer endpoint class. */ diff --git a/core/include/join/protocol.hpp b/core/include/join/protocol.hpp index ee2d53f3..0e888e70 100644 --- a/core/include/join/protocol.hpp +++ b/core/include/join/protocol.hpp @@ -36,49 +36,18 @@ namespace join { template class BasicSocket; + template class BasicDatagramSocket; + template class BasicStreamSocket; - template - class BasicTlsSocket; template class BasicSocketStream; - template - class BasicTlsStream; template class BasicStreamAcceptor; - template - class BasicTlsAcceptor; - - template - class BasicDatagramResolver; - template - class BasicTlsResolver; - - template - class BasicDatagramNameServer; - template - class BasicDatagramPeer; - - template - class BasicHttpClient; - template - class BasicHttpSecureClient; - - template - class BasicHttpWorker; - template - class BasicHttpServer; - template - class BasicHttpSecureServer; - - template - class BasicSmtpClient; - template - class BasicSmtpSecureClient; /** * @brief unix datagram protocol class. @@ -166,98 +135,6 @@ namespace join } }; - /** - * @brief netlink protocol class. - */ - class Netlink - { - public: - using Endpoint = BasicNetlinkEndpoint; - using Socket = BasicDatagramSocket; - - /** - * @brief construct the netlink protocol instance by default. - * @param proto protocol type. - */ - constexpr Netlink (int proto = NETLINK_ROUTE) noexcept - : _proto (proto) - { - } - - /** - * @brief get protocol suitable for netlink route. - * @return a netlink route protocol. - */ - static inline Netlink& rt () noexcept - { - static Netlink route (NETLINK_ROUTE); - return route; - } - - /** - * @brief get protocol suitable for netlink netfilter. - * @return a netlink route protocol. - */ - static inline Netlink& nf () noexcept - { - static Netlink netfilter (NETLINK_NETFILTER); - return netfilter; - } - - /** - * @brief get the protocol address family. - * @return the protocol address family. - */ - constexpr int family () const noexcept - { - return AF_NETLINK; - } - - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_RAW; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return _proto; - } - - private: - /// protocol. - int _proto; - }; - - /** - * @brief check if equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if equals. - */ - constexpr bool operator== (const Netlink& a, const Netlink& b) noexcept - { - return a.protocol () == b.protocol (); - } - - /** - * @brief check if not equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if not equals. - */ - constexpr bool operator!= (const Netlink& a, const Netlink& b) noexcept - { - return !(a == b); - } - /** * @brief RAW protocol class. */ @@ -586,790 +463,6 @@ namespace join { return !(a == b); } - - /** - * @brief SSL/TLS protocol class. - */ - class Tls - { - public: - using Endpoint = BasicInternetEndpoint; - using Socket = BasicTlsSocket; - using Stream = BasicTlsStream; - using Acceptor = BasicTlsAcceptor; - - /** - * @brief create the tcp protocol instance. - * @param family IP address family. - */ - constexpr Tls (int family = AF_INET) noexcept - : _family (family) - { - } - - /** - * @brief get protocol suitable for IPv4 address family. - * @return an IPv4 address family suitable protocol. - */ - static inline Tls& v4 () noexcept - { - static Tls tlsv4 (AF_INET); - return tlsv4; - } - - /** - * @brief get protocol suitable for IPv6 address family. - * @return an IPv6 address family suitable protocol. - */ - static inline Tls& v6 () noexcept - { - static Tls tlsv6 (AF_INET6); - return tlsv6; - } - - /** - * @brief get the protocol IP address family. - * @return the protocol IP address family. - */ - constexpr int family () const noexcept - { - return _family; - } - - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_STREAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_TCP; - } - - private: - /// IP address family. - int _family; - }; - - /** - * @brief check if equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if equals. - */ - constexpr bool operator== (const Tls& a, const Tls& b) noexcept - { - return a.family () == b.family (); - } - - /** - * @brief check if not equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if not equals. - */ - constexpr bool operator!= (const Tls& a, const Tls& b) noexcept - { - return !(a == b); - } - - /** - * @brief DNS over UDP protocol class. - */ - class Dns - { - public: - using Endpoint = BasicInternetEndpoint; - using Socket = BasicDatagramSocket; - using Resolver = BasicDatagramResolver; - using NameServer = BasicDatagramNameServer; - - /** - * @brief construct the DNS protocol instance. - * @param family IP address family. - */ - constexpr Dns (int family = AF_INET) noexcept - : _family (family) - { - } - - /** - * @brief get protocol suitable for IPv4 address family. - * @return an IPv4 address family suitable protocol. - */ - static inline Dns& v4 () noexcept - { - static Dns dnsv4 (AF_INET); - return dnsv4; - } - - /** - * @brief get protocol suitable for IPv6 address family. - * @return an IPv6 address family suitable protocol. - */ - static inline Dns& v6 () noexcept - { - static Dns dnsv6 (AF_INET6); - return dnsv6; - } - - /** - * @brief get the protocol IP address family. - * @return the protocol IP address family. - */ - constexpr int family () const noexcept - { - return _family; - } - - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_DGRAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_UDP; - } - - /// default DNS port. - static constexpr uint16_t defaultPort = 53; - - /// maximum DNS message size. - static constexpr size_t maxMsgSize = 8192; - - private: - /// IP address family. - int _family; - }; - - /** - * @brief check if equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if equals. - */ - constexpr bool operator== (const Dns& a, const Dns& b) noexcept - { - return a.family () == b.family (); - } - - /** - * @brief check if not equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if not equals. - */ - constexpr bool operator!= (const Dns& a, const Dns& b) noexcept - { - return !(a == b); - } - - /** - * @brief Multicast DNS protocol class - */ - class Mdns - { - public: - using Endpoint = BasicInternetEndpoint; - using Socket = BasicDatagramSocket; - using Peer = BasicDatagramPeer; - - /** - * @brief construct the mDNS protocol instance. - * @param family IP address family. - */ - constexpr Mdns (int family = AF_INET) noexcept - : _family (family) - { - } - - /** - * @brief get protocol suitable for IPv4 address family. - * @return an IPv4 address family suitable protocol. - */ - static inline Mdns& v4 () noexcept - { - static Mdns mdnsv4 (AF_INET); - return mdnsv4; - } - - /** - * @brief get protocol suitable for IPv6 address family. - * @return an IPv6 address family suitable protocol. - */ - static inline Mdns& v6 () noexcept - { - static Mdns mdnsv6 (AF_INET6); - return mdnsv6; - } - - /** - * @brief get the protocol IP address family. - * @return the protocol IP address family. - */ - constexpr int family () const noexcept - { - return _family; - } - - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_DGRAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_UDP; - } - - /** - * @brief get multicast address for the given address family. - * @param family IP address family. - * @return multicast IP address. - */ - static IpAddress multicastAddress (int family) noexcept - { - return (family == AF_INET6) ? "ff02::fb" : "224.0.0.251"; - } - - /// default DNS port. - static constexpr uint16_t defaultPort = 5353; - - /// maximum DNS message size. - static constexpr size_t maxMsgSize = 8192; - - private: - /// IP address family. - int _family; - }; - - /** - * @brief check if equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if equals. - */ - constexpr bool operator== (const Mdns& a, const Mdns& b) noexcept - { - return a.family () == b.family (); - } - - /** - * @brief check if not equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if not equals. - */ - constexpr bool operator!= (const Mdns& a, const Mdns& b) noexcept - { - return !(a == b); - } - - /** - * @brief DNS over TLS protocol class. - */ - class Dot - { - public: - using Endpoint = BasicInternetEndpoint; - using Socket = BasicTlsSocket; - using Resolver = BasicTlsResolver; - - /** - * @brief construct the DoT protocol instance. - * @param family IP address family. - */ - constexpr Dot (int family = AF_INET) noexcept - : _family (family) - { - } - - /** - * @brief get protocol suitable for IPv4 address family. - * @return an IPv4 address family suitable protocol. - */ - static inline Dot& v4 () noexcept - { - static Dot dotv4 (AF_INET); - return dotv4; - } - - /** - * @brief get protocol suitable for IPv6 address family. - * @return an IPv6 address family suitable protocol. - */ - static inline Dot& v6 () noexcept - { - static Dot dotv6 (AF_INET6); - return dotv6; - } - - /** - * @brief get the protocol IP address family. - * @return the protocol IP address family. - */ - constexpr int family () const noexcept - { - return _family; - } - - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_STREAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_TCP; - } - - /// default DoT port. - static constexpr uint16_t defaultPort = 853; - - /// maximum DoT message size. - static constexpr size_t maxMsgSize = 16384; - - private: - /// IP address family. - int _family; - }; - - /** - * @brief check if equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if equals. - */ - constexpr bool operator== (const Dot& a, const Dot& b) noexcept - { - return a.family () == b.family (); - } - - /** - * @brief check if not equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if not equals. - */ - constexpr bool operator!= (const Dot& a, const Dot& b) noexcept - { - return !(a == b); - } - - /** - * @brief HTTP protocol class. - */ - class Http - { - public: - using Endpoint = BasicInternetEndpoint; - using Socket = BasicStreamSocket; - using Stream = BasicSocketStream; - using Acceptor = BasicStreamAcceptor; - using Client = BasicHttpClient; - using Worker = BasicHttpWorker; - using Server = BasicHttpServer; - - /** - * @brief create the HTTP protocol instance. - * @param family IP address family. - */ - constexpr Http (int family = AF_INET) noexcept - : _family (family) - { - } - - /** - * @brief get protocol suitable for IPv4 address family. - * @return an IPv4 address family suitable protocol. - */ - static inline Http& v4 () noexcept - { - static Http httpv4 (AF_INET); - return httpv4; - } - - /** - * @brief get protocol suitable for IPv6 address family. - * @return an IPv6 address family suitable protocol. - */ - static inline Http& v6 () noexcept - { - static Http httpv6 (AF_INET6); - return httpv6; - } - - /** - * @brief get the protocol IP address family. - * @return the protocol IP address family. - */ - constexpr int family () const noexcept - { - return _family; - } - - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_STREAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_TCP; - } - - private: - /// IP address family. - int _family; - }; - - /** - * @brief check if equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if equals. - */ - constexpr bool operator== (const Http& a, const Http& b) noexcept - { - return a.family () == b.family (); - } - - /** - * @brief check if not equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if not equals. - */ - constexpr bool operator!= (const Http& a, const Http& b) noexcept - { - return !(a == b); - } - - /** - * @brief HTTPS protocol class. - */ - class Https - { - public: - using Endpoint = BasicInternetEndpoint; - using Socket = BasicTlsSocket; - using Stream = BasicTlsStream; - using Acceptor = BasicTlsAcceptor; - using Client = BasicHttpSecureClient; - using Worker = BasicHttpWorker; - using Server = BasicHttpSecureServer; - - /** - * @brief create the HTTPS protocol instance. - * @param family IP address family. - */ - constexpr Https (int family = AF_INET) noexcept - : _family (family) - { - } - - /** - * @brief get protocol suitable for IPv4 address family. - * @return an IPv4 address family suitable protocol. - */ - static inline Https& v4 () noexcept - { - static Https httpsv4 (AF_INET); - return httpsv4; - } - - /** - * @brief get protocol suitable for IPv6 address family. - * @return an IPv6 address family suitable protocol. - */ - static inline Https& v6 () noexcept - { - static Https httpsv6 (AF_INET6); - return httpsv6; - } - - /** - * @brief get the protocol IP address family. - * @return the protocol IP address family. - */ - constexpr int family () const noexcept - { - return _family; - } - - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_STREAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_TCP; - } - - private: - /// IP address family. - int _family; - }; - - /** - * @brief check if equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if equals. - */ - constexpr bool operator== (const Https& a, const Https& b) noexcept - { - return a.family () == b.family (); - } - - /** - * @brief check if not equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if not equals. - */ - constexpr bool operator!= (const Https& a, const Https& b) noexcept - { - return !(a == b); - } - - /** - * @brief SMTP protocol class. - */ - class Smtp - { - public: - using Endpoint = BasicInternetEndpoint; - using Socket = BasicTlsSocket; - using Stream = BasicTlsStream; - using Client = BasicSmtpClient; - - /** - * @brief create the SMTP protocol instance. - * @param family IP address family. - */ - constexpr Smtp (int family = AF_INET) noexcept - : _family (family) - { - } - - /** - * @brief get protocol suitable for IPv4 address family. - * @return an IPv4 address family suitable protocol. - */ - static inline Smtp& v4 () noexcept - { - static Smtp smtpv4 (AF_INET); - return smtpv4; - } - - /** - * @brief get protocol suitable for IPv6 address family. - * @return an IPv6 address family suitable protocol. - */ - static inline Smtp& v6 () noexcept - { - static Smtp smtpv6 (AF_INET6); - return smtpv6; - } - - /** - * @brief get the protocol IP address family. - * @return the protocol IP address family. - */ - constexpr int family () const noexcept - { - return _family; - } - - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_STREAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_TCP; - } - - private: - /// IP address family. - int _family; - }; - - /** - * @brief check if equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if equals. - */ - constexpr bool operator== (const Smtp& a, const Smtp& b) noexcept - { - return a.family () == b.family (); - } - - /** - * @brief check if not equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if not equals. - */ - constexpr bool operator!= (const Smtp& a, const Smtp& b) noexcept - { - return !(a == b); - } - - /** - * @brief SMTPS protocol class. - */ - class Smtps - { - public: - using Endpoint = BasicInternetEndpoint; - using Socket = BasicTlsSocket; - using Stream = BasicTlsStream; - using Client = BasicSmtpSecureClient; - - /** - * @brief create the SMTPS protocol instance. - * @param family IP address family. - */ - constexpr Smtps (int family = AF_INET) noexcept - : _family (family) - { - } - - /** - * @brief get protocol suitable for IPv4 address family. - * @return an IPv4 address family suitable protocol. - */ - static inline Smtps& v4 () noexcept - { - static Smtps smtpsv4 (AF_INET); - return smtpsv4; - } - - /** - * @brief get protocol suitable for IPv6 address family. - * @return an IPv6 address family suitable protocol. - */ - static inline Smtps& v6 () noexcept - { - static Smtps smtpsv6 (AF_INET6); - return smtpsv6; - } - - /** - * @brief get the protocol IP address family. - * @return the protocol IP address family. - */ - constexpr int family () const noexcept - { - return _family; - } - - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_STREAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_TCP; - } - - private: - /// IP address family. - int _family; - }; - - /** - * @brief check if equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if equals. - */ - constexpr bool operator== (const Smtps& a, const Smtps& b) noexcept - { - return a.family () == b.family (); - } - - /** - * @brief check if not equals. - * @param a protocol to check. - * @param b protocol to check. - * @return true if not equals. - */ - constexpr bool operator!= (const Smtps& a, const Smtps& b) noexcept - { - return !(a == b); - } } #endif diff --git a/core/include/join/socket.hpp b/core/include/join/socket.hpp index 38845378..dcbd4231 100644 --- a/core/include/join/socket.hpp +++ b/core/include/join/socket.hpp @@ -28,7 +28,6 @@ // libjoin. #include #include -#include #include #include @@ -1498,1079 +1497,6 @@ namespace join { return a.handle () < b.handle (); } - - /** - * @brief TLS error codes. - */ - enum class TlsErrc - { - TlsCloseNotifyAlert = 1, /**< A close notify alert was received. */ - TlsProtocolError /**< A failure in the TLS library occurred, usually a protocol error. */ - }; - - /** - * @brief TLS error category. - */ - class TlsCategory : public std::error_category - { - public: - /** - * @brief get digest error category name. - * @return digest error category name. - */ - virtual const char* name () const noexcept; - - /** - * @brief translate digest error code to human readable error string. - * @param code error code. - * @return human readable error string. - */ - virtual std::string message (int code) const; - }; - - /** - * @brief get error category. - * @return the created std::error_category object. - */ - const std::error_category& getTlsCategory (); - - /** - * @brief create an std::error_code object. - * @param code error code number. - * @return the created std::error_code object. - */ - std::error_code make_error_code (TlsErrc code); - - /** - * @brief create an std::error_condition object. - * @param code error code number. - * @return the created std::error_condition object. - */ - std::error_condition make_error_condition (TlsErrc code); - - /** - * @brief basic TLS socket class. - */ - template - class BasicTlsSocket : public BasicStreamSocket - { - public: - using Ptr = std::unique_ptr>; - using Proto = Protocol; - using Mode = typename BasicStreamSocket::Mode; - using Option = typename BasicStreamSocket::Option; - using State = typename BasicStreamSocket::State; - using Endpoint = typename Protocol::Endpoint; - - /** - * @brief default constructor. - */ - BasicTlsSocket () - : BasicTlsSocket (Mode::NonBlocking) - { - } - - /** - * @brief create instance specifying the mode. - * @param mode Set the socket blocking mode. - */ - BasicTlsSocket (Mode mode) - : BasicStreamSocket (mode) - , _tlsContext (SSL_CTX_new (TLS_client_method ()), SslCtxDelete ()) - { - // enable the OpenSSL bug workaround options. - SSL_CTX_set_options (this->_tlsContext.get (), SSL_OP_ALL); - - // disallow compression. - SSL_CTX_set_options (this->_tlsContext.get (), SSL_OP_NO_COMPRESSION); - - // disallow usage of SSLv2, SSLv3, TLSv1 and TLSv1.1 which are considered insecure. - SSL_CTX_set_options (this->_tlsContext.get (), - SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); - - // setup write mode. - SSL_CTX_set_mode (this->_tlsContext.get (), - SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - // automatically renegotiates. - SSL_CTX_set_mode (this->_tlsContext.get (), SSL_MODE_AUTO_RETRY); - - // set session cache mode to client by default. - SSL_CTX_set_session_cache_mode (this->_tlsContext.get (), SSL_SESS_CACHE_CLIENT); - - // no verification by default. - SSL_CTX_set_verify (this->_tlsContext.get (), SSL_VERIFY_NONE, nullptr); - - // set default TLSv1.2 and below cipher suites. - SSL_CTX_set_cipher_list (this->_tlsContext.get (), join::defaultCipher.c_str ()); - - // set default TLSv1.3 cipher suites. - SSL_CTX_set_ciphersuites (this->_tlsContext.get (), join::defaultCipher_1_3.c_str ()); - } - - /** - * @brief create instance specifying TLS context. - * @param tlsContext TLS context. - */ - BasicTlsSocket (join::SslCtxPtr tlsContext) - : BasicTlsSocket (Mode::NonBlocking, std::move (tlsContext)) - { - } - - /** - * @brief Create socket instance specifying the socket mode and TLS context. - * @param mode Set the socket blocking mode. - * @param tlsContext TLS context. - */ - BasicTlsSocket (Mode mode, join::SslCtxPtr tlsContext) - : BasicStreamSocket (mode) - , _tlsContext (std::move (tlsContext)) - { - if (this->_tlsContext == nullptr) - { - throw std::invalid_argument ("OpenSSL context is invalid"); - } - } - - /** - * @brief copy constructor. - * @param other other object to copy. - */ - BasicTlsSocket (const BasicTlsSocket& other) = delete; - - /** - * @brief copy assignment operator. - * @param other other object to assign. - * @return assigned object. - */ - BasicTlsSocket& operator= (const BasicTlsSocket& other) = delete; - - /** - * @brief move constructor. - * @param other other object to move. - */ - BasicTlsSocket (BasicTlsSocket&& other) - : BasicStreamSocket (std::move (other)) - , _tlsContext (std::move (other._tlsContext)) - , _tlsHandle (std::move (other._tlsHandle)) - , _tlsState (other._tlsState) - { - if (this->_tlsHandle) - { - SSL_set_app_data (this->_tlsHandle.get (), this); - } - - other._tlsState = TlsState::NonEncrypted; - } - - /** - * @brief move assignment operator. - * @param other other object to assign. - * @return assigned object. - */ - BasicTlsSocket& operator= (BasicTlsSocket&& other) - { - BasicStreamSocket::operator= (std::move (other)); - - this->_tlsContext = std::move (other._tlsContext); - this->_tlsHandle = std::move (other._tlsHandle); - this->_tlsState = other._tlsState; - - if (this->_tlsHandle) - { - SSL_set_app_data (this->_tlsHandle.get (), this); - } - - other._tlsState = TlsState::NonEncrypted; - - return *this; - } - - /** - * @brief destroy the instance. - */ - virtual ~BasicTlsSocket () = default; - - /** - * @brief make an encrypted connection to the given endpoint. - * @param endpoint endpoint to connect to. - * @return 0 on success, -1 on failure. - */ - virtual int connectEncrypted (const Endpoint& endpoint) - { - if (this->connect (endpoint) == -1) - { - return -1; - } - - if (this->startEncryption () == -1) - { - if (lastError != Errc::TemporaryError) - { - this->close (); - } - return -1; - } - - return 0; - } - - /** - * @brief start socket encryption (perform TLS handshake). - * @return 0 on success, -1 on failure. - */ - int startEncryption () - { - if (this->encrypted () == false) - { - this->_tlsHandle.reset (SSL_new (this->_tlsContext.get ())); - if (this->_tlsHandle == nullptr) - { - lastError = make_error_code (Errc::OutOfMemory); - return -1; - } - - if (SSL_set_fd (this->_tlsHandle.get (), this->_handle) != 1) - { - lastError = make_error_code (Errc::InvalidParam); - this->_tlsHandle.reset (); - return -1; - } - - if (SSL_is_server (this->_tlsHandle.get ()) == 0) - { - if (!this->_remote.hostname ().empty () && - (SSL_set_tlsext_host_name (this->_tlsHandle.get (), this->_remote.hostname ().c_str ()) != 1)) - { - lastError = make_error_code (Errc::InvalidParam); - this->_tlsHandle.reset (); - return -1; - } - - SSL_set_connect_state (this->_tlsHandle.get ()); - } - else - { - SSL_set_accept_state (this->_tlsHandle.get ()); - } - - SSL_set_app_data (this->_tlsHandle.get (), this); - -#ifdef DEBUG - SSL_set_info_callback (this->_tlsHandle.get (), infoWrapper); -#endif - - return startHandshake (); - } - - return 0; - } - - /** - * @brief wait until TLS handshake is performed or timeout occur (non blocking socket). - * @param timeout timeout in milliseconds (0: infinite). - * return true on success, false otherwise. - */ - virtual bool waitEncrypted (int timeout = 0) - { - if (this->encrypted () == false) - { - if (this->_state == State::Connecting) - { - if (!this->waitConnected (timeout)) - { - return false; - } - - if (this->startEncryption () == 0) - { - return true; - } - } - - while ((lastError == Errc::TemporaryError) && - (SSL_want_read (this->_tlsHandle.get ()) || SSL_want_write (this->_tlsHandle.get ()))) - { - if (this->wait (SSL_want_read (this->_tlsHandle.get ()), SSL_want_write (this->_tlsHandle.get ()), - timeout) == -1) - { - return false; - } - - if (this->startHandshake () == 0) - { - return true; - } - } - - return false; - } - - return true; - } - - /** - * @brief shutdown the connection. - * @return 0 on success, -1 on failure. - */ - virtual int disconnect () override - { - if (this->encrypted ()) - { - // check if the close_notify alert was already sent. - if ((SSL_get_shutdown (this->_tlsHandle.get ()) & SSL_SENT_SHUTDOWN) == false) - { - // send the close_notify alert to the peer. - int result = SSL_shutdown (this->_tlsHandle.get ()); - if (result < 0) - { - // shutdown was not successful. - switch (SSL_get_error (this->_tlsHandle.get (), result)) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - // SSL_shutdown want read or want write. - lastError = make_error_code (Errc::TemporaryError); - break; - case SSL_ERROR_SYSCALL: - // an error occurred at the socket level. - switch (errno) - { - case 0: - case ECONNRESET: - case EPIPE: - lastError = make_error_code (Errc::ConnectionClosed); - this->_tlsState = TlsState::NonEncrypted; - this->_state = State::Disconnected; - break; - default: - lastError = std::error_code (errno, std::generic_category ()); - break; - } - break; - default: - // SSL protocol error. -#ifdef DEBUG - std::cout << ERR_reason_error_string (ERR_get_error ()) << std::endl; -#endif - lastError = make_error_code (TlsErrc::TlsProtocolError); - break; - } - - return -1; - } - else if (result == 1) - { - // shutdown was successfully completed. - // close_notify alert was sent and the peer's close_notify alert was received. - this->_tlsState = TlsState::NonEncrypted; - } - else - { - // shutdown is not yet finished. - // the close_notify was sent but the peer did not send it back yet. - // SSL_read must be called to do a bidirectional shutdown. - } - } - } - - return BasicStreamSocket::disconnect (); - } - - /** - * @brief close the socket handle. - */ - virtual void close () noexcept override - { - BasicStreamSocket::close (); - this->_tlsState = TlsState::NonEncrypted; - this->_tlsHandle.reset (); - } - - /** - * @brief block until new data is available for reading. - * @param timeout timeout in milliseconds (0: infinite). - * @return true if there is new data available for reading, false otherwise. - */ - virtual bool waitReadyRead (int timeout = 0) const noexcept override - { - if (this->encrypted () && - (SSL_want_read (this->_tlsHandle.get ()) || SSL_want_write (this->_tlsHandle.get ()))) - { - return (this->wait (SSL_want_read (this->_tlsHandle.get ()), SSL_want_write (this->_tlsHandle.get ()), - timeout) == 0); - } - - return BasicStreamSocket::waitReadyRead (timeout); - } - - /** - * @brief get the number of readable bytes. - * @return The number of readable bytes, -1 on failure. - */ - virtual int canRead () const noexcept override - { - if (this->encrypted ()) - { - return SSL_pending (this->_tlsHandle.get ()); - } - - return BasicStreamSocket::canRead (); - } - - /** - * @brief read data on the socket. - * @param data buffer used to store the data received. - * @param maxSize maximum number of bytes to read. - * @return the number of bytes received, -1 on failure. - */ - virtual int read (char* data, unsigned long maxSize) noexcept override - { - if (this->encrypted ()) - { - // read data. - int result = SSL_read (this->_tlsHandle.get (), data, int (maxSize)); - if (result < 1) - { - switch (SSL_get_error (this->_tlsHandle.get (), result)) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_X509_LOOKUP: - // SSL_read want read, want write or want lookup. - lastError = make_error_code (Errc::TemporaryError); - break; - case SSL_ERROR_ZERO_RETURN: - // a close notify alert was received. - // we have to answer by sending a close notify alert too. - lastError = make_error_code (TlsErrc::TlsCloseNotifyAlert); - if (SSL_get_shutdown (this->_tlsHandle.get ()) & SSL_SENT_SHUTDOWN) - { - this->_tlsState = TlsState::NonEncrypted; - } - break; - case SSL_ERROR_SYSCALL: - // an error occurred at the socket level. - switch (errno) - { - case 0: - case ECONNRESET: - case EPIPE: - lastError = make_error_code (Errc::ConnectionClosed); - this->_tlsState = TlsState::NonEncrypted; - this->_state = State::Disconnected; - break; - default: - lastError = std::error_code (errno, std::generic_category ()); - break; - } - break; - default: - // SSL protocol error. -#ifdef DEBUG - std::cout << ERR_reason_error_string (ERR_get_error ()) << std::endl; -#endif - lastError = make_error_code (TlsErrc::TlsProtocolError); - break; - } - - return -1; - } - - return result; - } - - return BasicStreamSocket::read (data, maxSize); - } - - /** - * @brief block until until at least one byte can be written on the socket. - * @param timeout timeout in milliseconds (0: infinite). - * @return true if data can be written on the socket, false otherwise. - */ - virtual bool waitReadyWrite (int timeout = 0) const noexcept override - { - if (this->encrypted () && - (SSL_want_read (this->_tlsHandle.get ()) || SSL_want_write (this->_tlsHandle.get ()))) - { - return (this->wait (SSL_want_read (this->_tlsHandle.get ()), SSL_want_write (this->_tlsHandle.get ()), - timeout) == 0); - } - - return BasicStreamSocket::waitReadyWrite (timeout); - } - - /** - * @brief write data on the socket. - * @param data data buffer to send. - * @param maxSize maximum number of bytes to write. - * @return the number of bytes written, -1 on failure. - */ - virtual int write (const char* data, unsigned long maxSize) noexcept override - { - if (this->encrypted ()) - { - // write data. - int result = SSL_write (this->_tlsHandle.get (), data, int (maxSize)); - if (result < 1) - { - switch (SSL_get_error (this->_tlsHandle.get (), result)) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_X509_LOOKUP: - // SSL_write want read, want write or want lookup. - lastError = make_error_code (Errc::TemporaryError); - break; - case SSL_ERROR_ZERO_RETURN: - // a close notify alert was received. - // we have to answer by sending a close notify alert too. - lastError = make_error_code (TlsErrc::TlsCloseNotifyAlert); - if (SSL_get_shutdown (this->_tlsHandle.get ()) & SSL_SENT_SHUTDOWN) - { - this->_tlsState = TlsState::NonEncrypted; - } - break; - case SSL_ERROR_SYSCALL: - // an error occurred at the socket level. - switch (errno) - { - case 0: - case ECONNRESET: - case EPIPE: - lastError = make_error_code (Errc::ConnectionClosed); - this->_tlsState = TlsState::NonEncrypted; - this->_state = State::Disconnected; - break; - default: - lastError = std::error_code (errno, std::generic_category ()); - break; - } - break; - default: - // SSL protocol error. -#ifdef DEBUG - std::cout << ERR_reason_error_string (ERR_get_error ()) << std::endl; -#endif - lastError = make_error_code (TlsErrc::TlsProtocolError); - break; - } - - return -1; - } - - return result; - } - - return BasicStreamSocket::write (data, maxSize); - } - - /** - * @brief check if the socket is secure. - * @return true if the socket is secure, false otherwise. - */ - virtual bool encrypted () const noexcept override - { - return (this->_tlsState == TlsState::Encrypted); - } - - /** - * @brief set the certificate and the private key. - * @param cert certificate path. - * @param key private key path. - * @return 0 on success, -1 on failure. - */ - int setCertificate (const std::string& cert, const std::string& key = "") - { - if (((this->_tlsHandle) - ? SSL_use_certificate_file (this->_tlsHandle.get (), cert.c_str (), SSL_FILETYPE_PEM) - : SSL_CTX_use_certificate_file (this->_tlsContext.get (), cert.c_str (), SSL_FILETYPE_PEM)) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - if (key.size ()) - { - if (((this->_tlsHandle) - ? SSL_use_PrivateKey_file (this->_tlsHandle.get (), key.c_str (), SSL_FILETYPE_PEM) - : SSL_CTX_use_PrivateKey_file (this->_tlsContext.get (), key.c_str (), SSL_FILETYPE_PEM)) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - } - - if (((this->_tlsHandle) ? SSL_check_private_key (this->_tlsHandle.get ()) - : SSL_CTX_check_private_key (this->_tlsContext.get ())) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - return 0; - } - - /** - * @brief set the location of the trusted CA certificates. - * @param caPath path of the trusted CA certificates. - * @return 0 on success, -1 on failure. - */ - int setCaPath (const std::string& caPath) - { - struct stat st; - if (stat (caPath.c_str (), &st) != 0 || !S_ISDIR (st.st_mode) || - SSL_CTX_load_verify_locations (this->_tlsContext.get (), nullptr, caPath.c_str ()) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - return 0; - } - - /** - * @brief set the location of the trusted CA certificate file. - * @param caFile path of the trusted CA certificate file. - * @return 0 on success, -1 on failure. - */ - int setCaFile (const std::string& caFile) - { - struct stat st; - if (stat (caFile.c_str (), &st) != 0 || !S_ISREG (st.st_mode) || - SSL_CTX_load_verify_locations (this->_tlsContext.get (), caFile.c_str (), nullptr) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - return 0; - } - - /** - * @brief Enable/Disable the verification of the peer certificate. - * @param verify Enable peer verification if set to true, false otherwise. - * @param depth The maximum certificate verification depth (default: no limit). - */ - void setVerify (bool verify, int depth = -1) noexcept - { - if (verify == true) - { - SSL_CTX_set_verify (this->_tlsContext.get (), SSL_VERIFY_PEER, verifyWrapper); - SSL_CTX_set_verify_depth (this->_tlsContext.get (), depth); - } - else - { - SSL_CTX_set_verify (this->_tlsContext.get (), SSL_VERIFY_NONE, nullptr); - } - } - - /** - * @brief set the cipher list (TLSv1.2 and below). - * @param cipher the cipher list. - * @return 0 on success, -1 on failure. - */ - int setCipher (const std::string& cipher) - { - if (((this->_tlsHandle) ? SSL_set_cipher_list (this->_tlsHandle.get (), cipher.c_str ()) - : SSL_CTX_set_cipher_list (this->_tlsContext.get (), cipher.c_str ())) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - return 0; - } - - /** - * @brief set the cipher list (TLSv1.3). - * @param cipher the cipher list. - * @return 0 on success, -1 on failure. - */ - int setCipher_1_3 (const std::string& cipher) - { - if (((this->_tlsHandle) ? SSL_set_ciphersuites (this->_tlsHandle.get (), cipher.c_str ()) - : SSL_CTX_set_ciphersuites (this->_tlsContext.get (), cipher.c_str ())) == 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - return 0; - } - - /** - * @brief set the ALPN protocols list. - * @param protocols list of protocol names (ex. {"h2", "http/1.1"}). - * @return 0 on success, -1 on failure. - */ - int setAlpnProtocols (const std::vector& protocols) - { - std::vector wire; - wire.reserve (256); - - for (auto const& proto : protocols) - { - wire.push_back (static_cast (proto.size ())); - wire.insert (wire.end (), proto.begin (), proto.end ()); - } - - if (SSL_CTX_set_alpn_protos (this->_tlsContext.get (), wire.data (), - static_cast (wire.size ())) != 0) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - return 0; - } - - protected: - /** - * @brief TLS state. - */ - enum TlsState - { - Encrypted, /**< Socket is encrypted */ - NonEncrypted, /**< Socket is not encrypted */ - }; - - /** - * @brief Start SSL handshake. - * @return 0 on success, -1 on failure. - */ - int startHandshake () - { - // start the SSL handshake. - int result = SSL_do_handshake (this->_tlsHandle.get ()); - if (result < 1) - { - switch (SSL_get_error (this->_tlsHandle.get (), result)) - { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_X509_LOOKUP: - // SSL_do_handshake want read or want write. - lastError = make_error_code (Errc::TemporaryError); - break; - case SSL_ERROR_ZERO_RETURN: - // a close notify alert was received. - // we have to answer by sending a close notify alert too. - lastError = make_error_code (TlsErrc::TlsCloseNotifyAlert); - break; - case SSL_ERROR_SYSCALL: - // an error occurred at the socket level. - switch (errno) - { - case 0: - case ECONNRESET: - case EPIPE: - lastError = make_error_code (Errc::ConnectionClosed); - this->_state = State::Disconnected; - break; - default: - lastError = std::error_code (errno, std::generic_category ()); - break; - } - break; - default: - // SSL protocol error. -#ifdef DEBUG - std::cout << ERR_reason_error_string (ERR_get_error ()) << std::endl; -#endif - lastError = make_error_code (TlsErrc::TlsProtocolError); - break; - } - - return -1; - } - - this->_tlsState = TlsState::Encrypted; - - return 0; - } - - /** - * @brief c style callback wrapper for the state information callback. - * @param ssl SSL objects created from context during connection. - * @param where information about which context the callback function was called. - * @param ret error condition. - */ - static void infoWrapper (const SSL* ssl, int where, int ret) - { - assert (ssl); - static_cast*> (SSL_get_app_data (ssl))->infoCallback (where, ret); - } - - /** - * @brief state information callback. - * @param where information about which context the callback function was called. - * @param ret error condition. - */ - void infoCallback (int where, int ret) const - { - if (where & SSL_CB_ALERT) - { - std::cout << "SSL/TLS Alert "; - (where & SSL_CB_READ) ? std::cout << "[read] " : std::cout << "[write] "; - std::cout << SSL_alert_type_string_long (ret) << ":"; - std::cout << SSL_alert_desc_string_long (ret); - std::cout << std::endl; - } - else if (where & SSL_CB_LOOP) - { - std::cout << "SSL/TLS State "; - (SSL_in_connect_init (this->_tlsHandle.get ())) ? std::cout << "[connect] " - : (SSL_in_accept_init (this->_tlsHandle.get ())) ? std::cout << "[accept] " - : std::cout << "[undefined] "; - std::cout << SSL_state_string_long (this->_tlsHandle.get ()); - std::cout << std::endl; - } - else if (where & SSL_CB_HANDSHAKE_START) - { - std::cout << "SSL/TLS Handshake [Start] " << SSL_state_string_long (this->_tlsHandle.get ()) - << std::endl; - } - else if (where & SSL_CB_HANDSHAKE_DONE) - { - std::cout << "SSL/TLS Handshake [Done] " << SSL_state_string_long (this->_tlsHandle.get ()) - << std::endl; - std::cout << SSL_CTX_sess_number (this->_tlsContext.get ()) << " items in the session cache" - << std::endl; - std::cout << SSL_CTX_sess_connect (this->_tlsContext.get ()) << " client connects" << std::endl; - std::cout << SSL_CTX_sess_connect_good (this->_tlsContext.get ()) << " client connects that finished" - << std::endl; - std::cout << SSL_CTX_sess_connect_renegotiate (this->_tlsContext.get ()) - << " client renegotiations requested" << std::endl; - std::cout << SSL_CTX_sess_accept (this->_tlsContext.get ()) << " server connects" << std::endl; - std::cout << SSL_CTX_sess_accept_good (this->_tlsContext.get ()) << " server connects that finished" - << std::endl; - std::cout << SSL_CTX_sess_accept_renegotiate (this->_tlsContext.get ()) - << " server renegotiations requested" << std::endl; - std::cout << SSL_CTX_sess_hits (this->_tlsContext.get ()) << " session cache hits" << std::endl; - std::cout << SSL_CTX_sess_cb_hits (this->_tlsContext.get ()) << " external session cache hits" - << std::endl; - std::cout << SSL_CTX_sess_misses (this->_tlsContext.get ()) << " session cache misses" << std::endl; - std::cout << SSL_CTX_sess_timeouts (this->_tlsContext.get ()) << " session cache timeouts" << std::endl; - std::cout << "negotiated " << SSL_get_cipher (this->_tlsHandle.get ()) << " cipher suite" << std::endl; - } - } - - /** - * @brief c style callback wrapper for the Trusted CA certificates verification callback. - * @param preverifiedindicates, whether the verification of the certificate in question was passed or not. - * @param context pointer to the complete context used for the certificate chain verification. - * @return when verified successfully, the callback should return 1, 0 otherwise. - */ - static int verifyWrapper (int preverified, X509_STORE_CTX* context) - { - SSL* ssl = static_cast (X509_STORE_CTX_get_ex_data (context, SSL_get_ex_data_X509_STORE_CTX_idx ())); - - assert (ssl); - return static_cast*> (SSL_get_app_data (ssl)) - ->verifyCallback (preverified, context); - } - - /** - * @brief trusted CA certificates verification callback. - * @param preverified indicates, whether the verification of the certificate in question was passed or not. - * @param context pointer to the complete context used for the certificate chain verification. - * @return when verified successfully, the callback should return 1, 0 otherwise. - */ - int verifyCallback (int preverified, X509_STORE_CTX* context) const - { - int maxDepth = SSL_get_verify_depth (this->_tlsHandle.get ()); - int dpth = X509_STORE_CTX_get_error_depth (context); - -#ifdef DEBUG - std::cout << "verification started at depth=" << dpth << std::endl; -#endif - - // catch a too long certificate chain. - if ((maxDepth >= 0) && (dpth > maxDepth)) - { - preverified = 0; - X509_STORE_CTX_set_error (context, X509_V_ERR_CERT_CHAIN_TOO_LONG); - } - - if (!preverified) - { -#ifdef DEBUG - std::cout << "verification failed at depth=" << dpth << " - " - << X509_verify_cert_error_string (X509_STORE_CTX_get_error (context)) << std::endl; -#endif - return 0; - } - - // check the certificate host name. - if (!verifyCert (context)) - { -#ifdef DEBUG - std::cout << "rejected by CERT at depth=" << dpth << std::endl; -#endif - return 0; - } - - // check the revocation list. - /*if (!verifyCrl (context)) - { - #ifdef DEBUG - std::cout << "rejected by CRL at depth=" << dpth << std::endl; - #endif - return 0; - }*/ - - // check ocsp. - /*if (!verifyOcsp (context)) - { - #ifdef DEBUG - std::cout << "rejected by OCSP at depth=" << dpth << std::endl; - #endif - return 0; - }*/ - -#ifdef DEBUG - std::cout << "certificate accepted at depth=" << dpth << std::endl; -#endif - - return 1; - } - - /** - * @brief verify certificate validity. - * @param context pointer to the complete context used for the certificate chain verification. - * @return when verified successfully, the callback should return 1, 0 otherwise. - */ - int verifyCert (X509_STORE_CTX* context) const - { - int depth = X509_STORE_CTX_get_error_depth (context); - X509* cert = X509_STORE_CTX_get_current_cert (context); - - char buf[256]; - X509_NAME_oneline (X509_get_subject_name (cert), buf, sizeof (buf)); -#ifdef DEBUG - std::cout << "subject=" << buf << std::endl; -#endif - - // check the certificate host name - if (depth == 0) - { - // confirm a match between the hostname and the hostnames listed in the certificate. - if (!checkHostName (cert)) - { -#ifdef DEBUG - std::cout << "no match for hostname in the certificate" << std::endl; -#endif - return 0; - } - } - - return 1; - } - - /** - * @brief confirm a match between the hostname contacted and the hostnames listed in the certificate. - * @param certificate the server certificate. - * @return true if an alternative name matched the server hostname. - */ - bool checkHostName (X509* certificate) const - { - bool match = false; - - // get alternative names. - join::StackOfGeneralNamePtr altnames (reinterpret_cast ( - X509_get_ext_d2i (certificate, NID_subject_alt_name, 0, 0))); - if (altnames) - { - for (int i = 0; (i < sk_GENERAL_NAME_num (altnames.get ())) && !match; ++i) - { - // get a handle to alternative name. - GENERAL_NAME* current_name = sk_GENERAL_NAME_value (altnames.get (), i); - - if (current_name->type == GEN_DNS) - { - // get data and length. - const char* host = reinterpret_cast (ASN1_STRING_get0_data (current_name->d.ia5)); - size_t len = size_t (ASN1_STRING_length (current_name->d.ia5)); - std::string pattern (host, host + len), serverName (this->_remote.hostname ()); - - // strip off trailing dots. - if (pattern.back () == '.') - { - pattern.pop_back (); - } - - if (serverName.back () == '.') - { - serverName.pop_back (); - } - - // compare to pattern. - if (fnmatch (pattern.c_str (), serverName.c_str (), 0) == 0) - { - // an alternative name matched the server hostname. - match = true; - } - } - } - } - - return match; - } - - /** - * @brief verify certificate revocation using CRL. - * @param context pointer to the complete context used for the certificate chain verification. - * @return when verified successfully, the callback should return 1, 0 otherwise. - */ - /*int verifyCrl ([[maybe_unused]]X509_STORE_CTX *context) const - { - return 1; - }*/ - - /** - * @brief verify certificate revocation using OCSP. - * @param context pointer to the complete context used for the certificate chain verification. - * @return when verified successfully, the callback should return 1, 0 otherwise. - */ - /*int verifyOcsp ([[maybe_unused]]X509_STORE_CTX *context) const - { - return 1; - }*/ - - /// TLS context. - join::SslCtxPtr _tlsContext; - - /// TLS handle. - join::SslPtr _tlsHandle; - - /// TLS state. - TlsState _tlsState = TlsState::NonEncrypted; - - /// friendship with basic TLS acceptor - friend class BasicTlsAcceptor; - }; - - /** - * @brief compare if socket handle is inferior. - * @param a socket handle to compare. - * @param b socket handle to compare to. - * @return true if inferior. - */ - template - constexpr bool operator< (const BasicTlsSocket& a, const BasicTlsSocket& b) noexcept - { - return a.handle () < b.handle (); - } -} - -namespace std -{ - /// TLS error code specialization. - template <> - struct is_error_condition_enum : public true_type - { - }; } #endif diff --git a/core/include/join/socketstream.hpp b/core/include/join/socketstream.hpp index e35f04ef..29d27c8d 100644 --- a/core/include/join/socketstream.hpp +++ b/core/include/join/socketstream.hpp @@ -504,165 +504,6 @@ namespace join /// associated stream buffer. SocketStreambuf _sockbuf; }; - - /** - * @brief TLS stream class. - */ - template - class BasicTlsStream : public BasicSocketStream - { - public: - using SocketStreambuf = BasicSocketStreambuf; - using Endpoint = typename Protocol::Endpoint; - using Socket = typename Protocol::Socket; - - /** - * @brief default constructor. - */ - BasicTlsStream () - : BasicSocketStream () - { - } - - /** - * @brief copy constructor. - * @param other other object to copy. - */ - BasicTlsStream (const BasicTlsStream& other) = delete; - - /** - * @brief copy assignment operator. - * @param other other object to assign. - * @return current object. - */ - BasicTlsStream& operator= (const BasicTlsStream& other) = delete; - - /** - * @brief move constructor. - * @param other other object to move. - */ - BasicTlsStream (BasicTlsStream&& other) - : BasicSocketStream (std::move (other)) - { - } - - /** - * @brief move assignment operator. - * @param other other object to assign. - * @return current object. - */ - BasicTlsStream& operator= (BasicTlsStream&& other) - { - BasicSocketStream::operator= (std::move (other)); - - return *this; - } - - /** - * @brief destroy the TLS stream instance. - */ - virtual ~BasicTlsStream () = default; - - /** - * @brief start socket encryption (perform TLS handshake). - * @return 0 on success, -1 on failure. - */ - void startEncryption () - { - if (this->_sockbuf.socket ().startEncryption () == -1) - { - if (lastError == Errc::TemporaryError) - { - if (this->_sockbuf.socket ().waitEncrypted (this->timeout ())) - return; - } - - this->setstate (std::ios_base::failbit); - } - } - - /** - * @brief make an encrypted connection to the given endpoint. - * @param endpoint endpoint to connect to. - * @throw std::ios_base::failure. - */ - void connectEncrypted (const Endpoint& endpoint) - { - if (this->_sockbuf.connect (endpoint) == nullptr) - { - this->setstate (std::ios_base::failbit); - } - else - { - this->startEncryption (); - if (this->fail ()) - { - this->close (); - } - } - } - - /** - * @brief set the certificate and the private key. - * @param cert certificate path. - * @param key private key path. - * @return 0 on success, -1 on failure. - */ - int setCertificate (const std::string& cert, const std::string& key = "") - { - return this->_sockbuf.socket ().setCertificate (cert, key); - } - - /** - * @brief set the location of the trusted CA certificates. - * @param caPath path of the trusted CA certificates. - * @return 0 on success, -1 on failure. - */ - int setCaPath (const std::string& caPath) - { - return this->_sockbuf.socket ().setCaPath (caPath); - } - - /** - * @brief set the location of the trusted CA certificate file. - * @param caFile path of the trusted CA certificate file. - * @return 0 on success, -1 on failure. - */ - int setCaFile (const std::string& caFile) - { - return this->_sockbuf.socket ().setCaFile (caFile); - } - - /** - * @brief Enable/Disable the verification of the peer certificate. - * @param verify Enable peer verification if set to true, false otherwise. - * @param depth The maximum certificate verification depth (default: no limit). - */ - void setVerify (bool verify, int depth = -1) - { - return this->_sockbuf.socket ().setVerify (verify, depth); - } - - /** - * @brief set the cipher list (TLSv1.2 and below). - * @param cipher the cipher list. - * @return 0 on success, -1 on failure. - */ - int setCipher (const std::string& cipher) - { - return this->_sockbuf.socket ().setCipher (cipher); - } - - /** - * @brief set the cipher list (TLSv1.3). - * @param cipher the cipher list. - * @return 0 on success, -1 on failure. - */ - int setCipher_1_3 (const std::string& cipher) - { - return this->_sockbuf.socket ().setCipher_1_3 (cipher); - } - }; } #endif diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index 4610cb8c..0411efce 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -226,11 +226,6 @@ target_link_libraries(unixstreamsocket.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME unixstreamsocket.gtest COMMAND unixstreamsocket.gtest) install(TARGETS unixstreamsocket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(netlinksocket.gtest netlinksocket_test.cpp) -target_link_libraries(netlinksocket.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME netlinksocket.gtest COMMAND netlinksocket.gtest) -install(TARGETS netlinksocket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - add_executable(unixstreamacceptor.gtest unixstreamacceptor_test.cpp) target_link_libraries(unixstreamacceptor.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME unixstreamacceptor.gtest COMMAND unixstreamacceptor.gtest) @@ -266,31 +261,6 @@ target_link_libraries(tcpacceptor.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME tcpacceptor.gtest COMMAND tcpacceptor.gtest) install(TARGETS tcpacceptor.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(tlserror.gtest tlserror_test.cpp) -target_link_libraries(tlserror.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME tlserror.gtest COMMAND tlserror.gtest) -install(TARGETS tlserror.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(tlssocket.gtest tlssocket_test.cpp) -target_link_libraries(tlssocket.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME tlssocket.gtest COMMAND tlssocket.gtest) -install(TARGETS tlssocket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(tlssocketstream.gtest tlssocketstream_test.cpp) -target_link_libraries(tlssocketstream.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME tlssocketstream.gtest COMMAND tlssocketstream.gtest) -install(TARGETS tlssocketstream.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(tlsacceptor.gtest tlsacceptor_test.cpp) -target_link_libraries(tlsacceptor.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME tlsacceptor.gtest COMMAND tlsacceptor.gtest) -install(TARGETS tlsacceptor.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(openssl.gtest openssl_test.cpp) -target_link_libraries(openssl.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME openssl.gtest COMMAND openssl.gtest) -install(TARGETS openssl.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - add_executable(cpu.gtest cpu_test.cpp) target_link_libraries(cpu.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME cpu.gtest COMMAND cpu.gtest) diff --git a/core/tests/endpoint_test.cpp b/core/tests/endpoint_test.cpp index 7df5cdc0..6aa5ac39 100644 --- a/core/tests/endpoint_test.cpp +++ b/core/tests/endpoint_test.cpp @@ -34,8 +34,6 @@ using join::Raw; using join::Udp; using join::Icmp; using join::Tcp; -using join::Tls; -using join::Netlink; /** * @brief test the addr method. @@ -59,12 +57,6 @@ TEST (Endpoint, addr) Tcp::Endpoint tcpEndpoint; ASSERT_NE (tcpEndpoint.addr (), nullptr); - - Tls::Endpoint tlsEndpoint; - ASSERT_NE (tlsEndpoint.addr (), nullptr); - - Netlink::Endpoint netlinkEndpoint; - ASSERT_NE (netlinkEndpoint.addr (), nullptr); } /** @@ -98,15 +90,6 @@ TEST (Endpoint, length) Tcp::Endpoint tcpEndpoint6 (Tcp::v6 ()); ASSERT_EQ (tcpEndpoint6.length (), sizeof (struct sockaddr_in6)); - - Tls::Endpoint tlsEndpoint4 (Tls::v4 ()); - ASSERT_EQ (tlsEndpoint4.length (), sizeof (struct sockaddr_in)); - - Tls::Endpoint tlsEndpoint6 (Tls::v6 ()); - ASSERT_EQ (tlsEndpoint6.length (), sizeof (struct sockaddr_in6)); - - Netlink::Endpoint netlinkEndpoint; - ASSERT_EQ (netlinkEndpoint.length (), sizeof (struct sockaddr_nl)); } /** @@ -143,14 +126,6 @@ TEST (Endpoint, device) ASSERT_EQ (tcpEndpoint.device (), ""); tcpEndpoint.device ("lo"); ASSERT_EQ (tcpEndpoint.device (), "lo"); - - Tls::Endpoint tlsEndpoint (Tls::v6 ()); - ASSERT_EQ (tlsEndpoint.device (), ""); - tlsEndpoint.device ("lo"); - ASSERT_EQ (tlsEndpoint.device (), "lo"); - - Netlink::Endpoint netlinkEndpoint; - ASSERT_EQ (netlinkEndpoint.device (), ""); } /** @@ -178,13 +153,6 @@ TEST (Endpoint, ip) ASSERT_EQ (tcpEndpoint.ip (), "::"); tcpEndpoint.ip ("127.0.0.1"); ASSERT_EQ (tcpEndpoint.ip (), "127.0.0.1"); - - Tls::Endpoint tlsEndpoint; - - tlsEndpoint.ip ("::"); - ASSERT_EQ (tlsEndpoint.ip (), "::"); - tlsEndpoint.ip ("127.0.0.1"); - ASSERT_EQ (tlsEndpoint.ip (), "127.0.0.1"); } /** @@ -207,14 +175,6 @@ TEST (Endpoint, port) Tcp::Endpoint tcpEndpoint6 (Tcp::v6 ()); tcpEndpoint6.port (443); ASSERT_EQ (tcpEndpoint6.port (), 443); - - Tls::Endpoint tlsEndpoint4 (Tls::v4 ()); - tlsEndpoint4.port (80); - ASSERT_EQ (tlsEndpoint4.port (), 80); - - Tls::Endpoint tlsEndpoint6 (Tls::v6 ()); - tlsEndpoint6.port (443); - ASSERT_EQ (tlsEndpoint6.port (), 443); } /** @@ -248,22 +208,6 @@ TEST (Endpoint, protocol) ASSERT_NE (Tcp::Endpoint ("127.0.0.1").protocol (), Tcp::v6 ()); ASSERT_NE (Tcp::Endpoint ("::").protocol (), Tcp::v4 ()); ASSERT_EQ (Tcp::Endpoint ("::").protocol (), Tcp::v6 ()); - - ASSERT_EQ (Tls::Endpoint ().protocol (), Tls::v4 ()); - ASSERT_EQ (Tls::Endpoint (Tls::v4 ()).protocol (), Tls::v4 ()); - ASSERT_NE (Tls::Endpoint (Tls::v4 ()).protocol (), Tls::v6 ()); - ASSERT_EQ (Tls::Endpoint (Tls::v6 ()).protocol (), Tls::v6 ()); - ASSERT_EQ (Tls::Endpoint ("127.0.0.1").protocol (), Tls::v4 ()); - ASSERT_NE (Tls::Endpoint ("127.0.0.1").protocol (), Tls::v6 ()); - ASSERT_NE (Tls::Endpoint ("::").protocol (), Tls::v4 ()); - ASSERT_EQ (Tls::Endpoint ("::").protocol (), Tls::v6 ()); - - ASSERT_EQ (Netlink::Endpoint ().protocol (), Netlink::rt ()); - ASSERT_EQ (Netlink::Endpoint (Netlink::rt (), RTMGRP_LINK).protocol (), Netlink::rt ()); - ASSERT_NE (Netlink::Endpoint (Netlink::rt (), RTMGRP_LINK).protocol (), Netlink::nf ()); - ASSERT_EQ (Netlink::Endpoint (Netlink::nf (), NFNLGRP_NONE).protocol (), Netlink::nf ()); - ASSERT_EQ (Netlink::Endpoint (RTMGRP_LINK).protocol (), Netlink::rt ()); - ASSERT_NE (Netlink::Endpoint (RTMGRP_LINK).protocol (), Netlink::nf ()); } /** @@ -295,16 +239,6 @@ TEST (Endpoint, equal) ASSERT_NE (Tcp::Endpoint ("127.0.0.1", 80), Tcp::Endpoint ("fe80::57f3:baa4:fc3a:890a", 443)); ASSERT_EQ (Tcp::Endpoint ("fe80::57f3:baa4:fc3a:890a", 443), Tcp::Endpoint ("fe80::57f3:baa4:fc3a:890a", 443)); ASSERT_NE (Tcp::Endpoint ("fe80::57f3:baa4:fc3a:890a", 443), Tcp::Endpoint ("127.0.0.1", 80)); - - ASSERT_EQ (Tls::Endpoint ("127.0.0.1", 80), Tls::Endpoint ("127.0.0.1", 80)); - ASSERT_NE (Tls::Endpoint ("127.0.0.1", 80), Tls::Endpoint ("fe80::57f3:baa4:fc3a:890a", 443)); - ASSERT_EQ (Tls::Endpoint ("fe80::57f3:baa4:fc3a:890a", 443), Tls::Endpoint ("fe80::57f3:baa4:fc3a:890a", 443)); - ASSERT_NE (Tls::Endpoint ("fe80::57f3:baa4:fc3a:890a", 443), Tls::Endpoint ("127.0.0.1", 80)); - - ASSERT_EQ (Netlink::Endpoint (RTMGRP_LINK), Netlink::Endpoint (RTMGRP_LINK)); - ASSERT_NE (Netlink::Endpoint (RTMGRP_LINK), Netlink::Endpoint (RTMGRP_IPV4_IFADDR)); - ASSERT_EQ (Netlink::Endpoint (RTMGRP_IPV4_IFADDR), Netlink::Endpoint (RTMGRP_IPV4_IFADDR)); - ASSERT_NE (Netlink::Endpoint (RTMGRP_IPV4_IFADDR), Netlink::Endpoint (RTMGRP_LINK)); } /** @@ -356,23 +290,6 @@ TEST (Endpoint, serialize) tcpEndpoint.ip ("::"); ASSERT_NO_THROW (stream << tcpEndpoint); ASSERT_EQ (stream.str (), "[::]:80"); - - stream.str (""); - Tls::Endpoint tlsEndpoint ("127.0.0.1", 80); - ASSERT_NO_THROW (stream << tlsEndpoint); - ASSERT_EQ (stream.str (), "127.0.0.1:80"); - - stream.str (""); - tlsEndpoint.ip ("::"); - ASSERT_NO_THROW (stream << tlsEndpoint); - ASSERT_EQ (stream.str (), "[::]:80"); - - stream.str (""); - Netlink::Endpoint netlinkEndpoint (RTMGRP_LINK); - ASSERT_NO_THROW (stream << netlinkEndpoint); - std::stringstream ss; - ss << "pid=" << getpid () << ",groups=" << uint32_t (RTMGRP_LINK); - ASSERT_EQ (stream.str (), ss.str ()); } /** diff --git a/core/tests/protocol_test.cpp b/core/tests/protocol_test.cpp index 42e13f25..7aab2014 100644 --- a/core/tests/protocol_test.cpp +++ b/core/tests/protocol_test.cpp @@ -34,15 +34,6 @@ using join::Raw; using join::Udp; using join::Icmp; using join::Tcp; -using join::Tls; -using join::Dns; -using join::Mdns; -using join::Dot; -using join::Http; -using join::Https; -using join::Smtp; -using join::Smtps; -using join::Netlink; /** * @brief test the family method. @@ -61,31 +52,6 @@ TEST (Protocol, family) ASSERT_EQ (Tcp ().family (), AF_INET); ASSERT_EQ (Tcp::v6 ().family (), AF_INET6); ASSERT_EQ (Tcp::v4 ().family (), AF_INET); - ASSERT_EQ (Tls ().family (), AF_INET); - ASSERT_EQ (Tls::v6 ().family (), AF_INET6); - ASSERT_EQ (Tls::v4 ().family (), AF_INET); - ASSERT_EQ (Dns ().family (), AF_INET); - ASSERT_EQ (Dns::v6 ().family (), AF_INET6); - ASSERT_EQ (Dns::v4 ().family (), AF_INET); - ASSERT_EQ (Mdns ().family (), AF_INET); - ASSERT_EQ (Mdns::v6 ().family (), AF_INET6); - ASSERT_EQ (Mdns::v4 ().family (), AF_INET); - ASSERT_EQ (Dot ().family (), AF_INET); - ASSERT_EQ (Dot::v6 ().family (), AF_INET6); - ASSERT_EQ (Dot::v4 ().family (), AF_INET); - ASSERT_EQ (Http ().family (), AF_INET); - ASSERT_EQ (Http::v6 ().family (), AF_INET6); - ASSERT_EQ (Http::v4 ().family (), AF_INET); - ASSERT_EQ (Https ().family (), AF_INET); - ASSERT_EQ (Https::v6 ().family (), AF_INET6); - ASSERT_EQ (Https::v4 ().family (), AF_INET); - ASSERT_EQ (Smtp ().family (), AF_INET); - ASSERT_EQ (Smtp::v6 ().family (), AF_INET6); - ASSERT_EQ (Smtp::v4 ().family (), AF_INET); - ASSERT_EQ (Smtps ().family (), AF_INET); - ASSERT_EQ (Smtps::v6 ().family (), AF_INET6); - ASSERT_EQ (Smtps::v4 ().family (), AF_INET); - ASSERT_EQ (Netlink::rt ().family (), AF_NETLINK); } /** @@ -99,15 +65,6 @@ TEST (Protocol, type) ASSERT_EQ (Udp ().type (), SOCK_DGRAM); ASSERT_EQ (Icmp ().type (), SOCK_RAW); ASSERT_EQ (Tcp ().type (), SOCK_STREAM); - ASSERT_EQ (Tls ().type (), SOCK_STREAM); - ASSERT_EQ (Dns ().type (), SOCK_DGRAM); - ASSERT_EQ (Mdns ().type (), SOCK_DGRAM); - ASSERT_EQ (Dot ().type (), SOCK_STREAM); - ASSERT_EQ (Http ().type (), SOCK_STREAM); - ASSERT_EQ (Https ().type (), SOCK_STREAM); - ASSERT_EQ (Smtp ().type (), SOCK_STREAM); - ASSERT_EQ (Smtps ().type (), SOCK_STREAM); - ASSERT_EQ (Netlink ().type (), SOCK_RAW); } /** @@ -122,17 +79,6 @@ TEST (Protocol, protocol) ASSERT_EQ (Icmp::v6 ().protocol (), IPPROTO_ICMPV6); ASSERT_EQ (Icmp::v4 ().protocol (), IPPROTO_ICMP); ASSERT_EQ (Tcp ().protocol (), IPPROTO_TCP); - ASSERT_EQ (Tls ().protocol (), IPPROTO_TCP); - ASSERT_EQ (Dns ().protocol (), IPPROTO_UDP); - ASSERT_EQ (Mdns ().protocol (), IPPROTO_UDP); - ASSERT_EQ (Dot ().protocol (), IPPROTO_TCP); - ASSERT_EQ (Http ().protocol (), IPPROTO_TCP); - ASSERT_EQ (Https ().protocol (), IPPROTO_TCP); - ASSERT_EQ (Smtp ().protocol (), IPPROTO_TCP); - ASSERT_EQ (Smtps ().protocol (), IPPROTO_TCP); - ASSERT_EQ (Netlink ().protocol (), NETLINK_ROUTE); - ASSERT_EQ (Netlink::rt ().protocol (), NETLINK_ROUTE); - ASSERT_EQ (Netlink::nf ().protocol (), NETLINK_NETFILTER); } /** @@ -154,51 +100,6 @@ TEST (Protocol, equal) ASSERT_NE (Tcp::v4 (), Tcp::v6 ()); ASSERT_EQ (Tcp::v6 (), Tcp::v6 ()); ASSERT_NE (Tcp::v6 (), Tcp::v4 ()); - - ASSERT_EQ (Tls::v4 (), Tls::v4 ()); - ASSERT_NE (Tls::v4 (), Tls::v6 ()); - ASSERT_EQ (Tls::v6 (), Tls::v6 ()); - ASSERT_NE (Tls::v6 (), Tls::v4 ()); - - ASSERT_EQ (Dns::v4 (), Dns::v4 ()); - ASSERT_NE (Dns::v4 (), Dns::v6 ()); - ASSERT_EQ (Dns::v6 (), Dns::v6 ()); - ASSERT_NE (Dns::v6 (), Dns::v4 ()); - - ASSERT_EQ (Mdns::v4 (), Mdns::v4 ()); - ASSERT_NE (Mdns::v4 (), Mdns::v6 ()); - ASSERT_EQ (Mdns::v6 (), Mdns::v6 ()); - ASSERT_NE (Mdns::v6 (), Mdns::v4 ()); - - ASSERT_EQ (Dot::v4 (), Dot::v4 ()); - ASSERT_NE (Dot::v4 (), Dot::v6 ()); - ASSERT_EQ (Dot::v6 (), Dot::v6 ()); - ASSERT_NE (Dot::v6 (), Dot::v4 ()); - - ASSERT_EQ (Http::v4 (), Http::v4 ()); - ASSERT_NE (Http::v4 (), Http::v6 ()); - ASSERT_EQ (Http::v6 (), Http::v6 ()); - ASSERT_NE (Http::v6 (), Http::v4 ()); - - ASSERT_EQ (Https::v4 (), Https::v4 ()); - ASSERT_NE (Https::v4 (), Https::v6 ()); - ASSERT_EQ (Https::v6 (), Https::v6 ()); - ASSERT_NE (Https::v6 (), Https::v4 ()); - - ASSERT_EQ (Smtp::v4 (), Smtp::v4 ()); - ASSERT_NE (Smtp::v4 (), Smtp::v6 ()); - ASSERT_EQ (Smtp::v6 (), Smtp::v6 ()); - ASSERT_NE (Smtp::v6 (), Smtp::v4 ()); - - ASSERT_EQ (Smtps::v4 (), Smtps::v4 ()); - ASSERT_NE (Smtps::v4 (), Smtps::v6 ()); - ASSERT_EQ (Smtps::v6 (), Smtps::v6 ()); - ASSERT_NE (Smtps::v6 (), Smtps::v4 ()); - - ASSERT_EQ (Netlink::rt (), Netlink::rt ()); - ASSERT_NE (Netlink::rt (), Netlink::nf ()); - ASSERT_EQ (Netlink::nf (), Netlink::nf ()); - ASSERT_NE (Netlink::nf (), Netlink::rt ()); } /** diff --git a/core/tests/tlsacceptor_test.cpp b/core/tests/tlsacceptor_test.cpp deleted file mode 100644 index b4a5e4ec..00000000 --- a/core/tests/tlsacceptor_test.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 Mathieu Rabine - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// libjoin. -#include - -// Libraries. -#include - -// C++. -#include - -using join::Errc; -using join::IpAddress; -using join::Tls; - -/** - * @brief Class used to test the TLS socket API. - */ -class TlsAcceptor : public ::testing::Test -{ -public: - /** - * @brief set up test case. - */ - static void SetUpTestCase () - { - std::ofstream rootCertFile (_root); - if (rootCertFile.is_open ()) - { - rootCertFile << "-----BEGIN CERTIFICATE-----" << std::endl; - rootCertFile << "MIIChjCCAisCFBuHxbqMUGyl7OQUQcoRg3pOBJF+MAoGCCqGSM49BAMCMIHEMQsw" << std::endl; - rootCertFile << "CQYDVQQGEwJGUjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVz" << std::endl; - rootCertFile << "MRcwFQYDVQQKDA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdv" << std::endl; - rootCertFile << "cmsgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3" << std::endl; - rootCertFile << "b3JrLm5ldDEoMCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5l" << std::endl; - rootCertFile << "dDAeFw0yMjA3MDUxNjMxMTZaFw0zMjA3MDIxNjMxMTZaMIHEMQswCQYDVQQGEwJG" << std::endl; - rootCertFile << "UjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVzMRcwFQYDVQQK" << std::endl; - rootCertFile << "DA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdvcmsgQ2VydGlm" << std::endl; - rootCertFile << "aWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3b3JrLm5ldDEo" << std::endl; - rootCertFile << "MCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5ldDBZMBMGByqG" << std::endl; - rootCertFile << "SM49AgEGCCqGSM49AwEHA0IABASk0zCrKtXQi0Ycx+Anx+VWv8gncbPmNQ1yutii" << std::endl; - rootCertFile << "gQjP2mF9NIqlxpcKNuE/6DDnfSzCEDhFyvGiK0NJ1C3RBowwCgYIKoZIzj0EAwID" << std::endl; - rootCertFile << "SQAwRgIhAIFqdbxTb5kRjy4UY0N205ZEhHSMK89p2oUyn4iNbXH2AiEAtmV1UyRX" << std::endl; - rootCertFile << "DIAGr/F+1SwQMPoJzSQxZ7NdxjNgW286e9Q=" << std::endl; - rootCertFile << "-----END CERTIFICATE-----" << std::endl; - rootCertFile.close (); - } - - std::ofstream certFile (_cert); - if (certFile.is_open ()) - { - certFile << "-----BEGIN CERTIFICATE-----" << std::endl; - certFile << "MIIDgDCCAyagAwIBAgIUR3ZIuKMt0BdaOZQnPwhSMR9qzfgwCgYIKoZIzj0EAwIw" << std::endl; - certFile << "gcQxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nh" << std::endl; - certFile << "c3RyZXMxFzAVBgNVBAoMDkpvaW4gRnJhbWV3b3JrMS0wKwYDVQQLDCRKb2luIEZy" << std::endl; - certFile << "YW1ld29yayBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHTAbBgNVBAMMFGNhLmpvaW5m" << std::endl; - certFile << "cmFtZXdvcmsubmV0MSgwJgYJKoZIhvcNAQkBFhlzdXBwb3J0QGpvaW5mcmFtZXdv" << std::endl; - certFile << "cmsubmV0MB4XDTIyMDcwNzEyMTIxMFoXDTMyMDcwNDEyMTIxMFowgagxCzAJBgNV" << std::endl; - certFile << "BAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nhc3RyZXMxFzAV" << std::endl; - certFile << "BgNVBAoMDkpvaW4gRnJhbWV3b3JrMRswGQYDVQQLDBJKb2luIEZyYW1ld29yayBE" << std::endl; - certFile << "ZXYxEzARBgNVBAMMCmxvY2FsaG9zdC4xKDAmBgkqhkiG9w0BCQEWGXN1cHBvcnRA" << std::endl; - certFile << "am9pbmZyYW1ld29yay5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" << std::endl; - certFile << "AQDSNtw5zEoJFPf6Rl0Y1n8BQfE0YTPCELvFAeioUfj8CAnUleHL9pwAEFg6kgoG" << std::endl; - certFile << "hvwto5/yWGPUqNNfe3xbFTJcHgMhgtjqy5H6sYDkTi3kYIIMBfTHr8NI7HWE8Nz1" << std::endl; - certFile << "qU1snjtERnkoLilIZf/2BojNVMtHC1H316WbMicXS0v7HQo3lv6PYSana9Q9ow9O" << std::endl; - certFile << "2/FiW5qq1eOhI1ZedRanX+bl0jHWCd3WsI87+5bTaQrfetdHTOmav6O17Iq9FiTh" << std::endl; - certFile << "Sg9fbM3s2Hw15kI+mws029dhcwXs5sYY+NgtrQwjR5qH+54BdUaPwQfl/KyulfEl" << std::endl; - certFile << "TJykJ+3w6MorxUr55F68uBNbAgMBAAGjRTBDMAsGA1UdDwQEAwIF4DAdBgNVHSUE" << std::endl; - certFile << "FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwFQYDVR0RBA4wDIIKbG9jYWxob3N0LjAK" << std::endl; - certFile << "BggqhkjOPQQDAgNIADBFAiA120ufIbhcw7BJQ1L6WudDdW2mHrVXvdgeOzVGgz1d" << std::endl; - certFile << "iAIhAMm/sWI3yzb2IMPffxWKYusWEQE2hZvs24ESSC/ZZ0s+" << std::endl; - certFile << "-----END CERTIFICATE-----" << std::endl; - certFile.close (); - } - - std::ofstream keyFile (_key); - if (keyFile.is_open ()) - { - keyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; - keyFile << "MIIEowIBAAKCAQEA0jbcOcxKCRT3+kZdGNZ/AUHxNGEzwhC7xQHoqFH4/AgJ1JXh" << std::endl; - keyFile << "y/acABBYOpIKBob8LaOf8lhj1KjTX3t8WxUyXB4DIYLY6suR+rGA5E4t5GCCDAX0" << std::endl; - keyFile << "x6/DSOx1hPDc9alNbJ47REZ5KC4pSGX/9gaIzVTLRwtR99elmzInF0tL+x0KN5b+" << std::endl; - keyFile << "j2Emp2vUPaMPTtvxYluaqtXjoSNWXnUWp1/m5dIx1gnd1rCPO/uW02kK33rXR0zp" << std::endl; - keyFile << "mr+jteyKvRYk4UoPX2zN7Nh8NeZCPpsLNNvXYXMF7ObGGPjYLa0MI0eah/ueAXVG" << std::endl; - keyFile << "j8EH5fysrpXxJUycpCft8OjKK8VK+eRevLgTWwIDAQABAoIBAAzdlK7o5OMXaHHl" << std::endl; - keyFile << "2o7Jme5Oxd9pz4wiEAvnqQCcO7vZFhjvr2kXR8btOSkkhP6PRmHYsNJZPIroZj9i" << std::endl; - keyFile << "xGKisnlW0OQ9KN995ApO0M+oRUDD81GfD7Mk+7O73Rls0GksmnN6X7A3C/U8lgQ7" << std::endl; - keyFile << "UeYR0k+Wz/YiKDsd9KHB+QiA8D6HFQ9I8Y2P97KOcYnxXZfSwNm+ENNU3wShZOl2" << std::endl; - keyFile << "ZYJJ4DE+5m2SwZ6g8b5Zre4cDbOduwuz/jXzjy2tAZBlTS4DVpYlhd14z+ssUWiu" << std::endl; - keyFile << "AdS/nqSF7Obj0TRhoGNfrkisFzV4itavQ5DKGj/6hjueIJVLteUOzcCeg26YosNy" << std::endl; - keyFile << "QzZSjOECgYEA7y3InEoh93/4HZCZmdwN8KfZtqirX0t966FntgAT8RkIs+KvNS8B" << std::endl; - keyFile << "m3RfNLa/EuDt5zTmHRGx+oeN+17i9QQjKWcR0NnJ6aSZbvJByj3yKxLF9XVllzp/" << std::endl; - keyFile << "vHSSyB264RoKIrWmFN6cCO4u4h9ZPY75pASWBCDMdnGK8axAcqAnlqsCgYEA4P+Y" << std::endl; - keyFile << "FF9RW4rhrVU4dpXSfcr6vOwqfp9F9vhTVL0JS/SLOFoJNNpS9Rnq3pVLEuKyCphd" << std::endl; - keyFile << "3nk9VFfoRygmMaGBvwGaXZPPvosoaIUgOdTt7KIfSHPichBEVxRuWCrtTGGkG0ok" << std::endl; - keyFile << "s/RPHhvxZE267vsVj1PktK8Yr5Ba0AL2ycztNhECgYB5OAwHYe8LIBlg6otelk+e" << std::endl; - keyFile << "W4OU9rE8L+eWx4vniuyQce6eNNI1syguYHFsJv56E/OfDYlezDwWzCLidnmyUjF7" << std::endl; - keyFile << "51f5MJgLyTdWKoO7e1/EAtS/jYs6dRSOL8rAj4jKU0c1xjhxNU2BnS23vsmc0Fyn" << std::endl; - keyFile << "iwd4+iKGGQ+hYnqbXZ4S1wKBgD/3an0gPDkSWua0e8D7B0TMGEztt4cYMQPtxYMp" << std::endl; - keyFile << "2yLE+2+h6UwlZcBZBfUR7K4J1SQ9/THqtgzskRTpzTH/AKwVAJXqF/3MAkj00Byg" << std::endl; - keyFile << "9KN50/r9NzvGdCdtn5FhYuV8PPOlOJoQsw2UVCR4FNUsfQyqhTL5NMN0/tx0e0UU" << std::endl; - keyFile << "BbyBAoGBANu5ifByauVELH8UEl5rXRu1S9iAVV+Bc5jboXwc4VxJtEyomGJ7+YdL" << std::endl; - keyFile << "5c9LFV+STUp7CE12uSXQZTQM0tEjPinLntRinNzu9tIHR1vy7FZHEwMFIgB4VTY7" << std::endl; - keyFile << "ALRYv1/QpTuywpNUFRS15JkfGNf5JIkrUEWLgkX3OVCBsRGHUugy" << std::endl; - keyFile << "-----END RSA PRIVATE KEY-----" << std::endl; - keyFile.close (); - } - - std::ofstream invalidKeyFile (_invalidKey); - if (invalidKeyFile.is_open ()) - { - invalidKeyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; - invalidKeyFile << "MIIEowIBAAKCAQEA2Q0DOyG039uVMuxNnZ5fpfOcvXXOTguST1QR6eLVkdG7OKpM" << std::endl; - invalidKeyFile << "nc9K597jx1syT1q+SwFcykMtvWxCfD8BR7bcLILeO6z+HlRfvjOhUiHaX/KCaTN8" << std::endl; - invalidKeyFile << "l7OJOgmUlL0FhQ1SXxw7KCSGd+rgu1iHwjFDDkj/tG24ashdmNt+DYdeoJu2mzgw" << std::endl; - invalidKeyFile << "tEASfG9VjqBR7ni4Hg/sRpwXvEK5nI1JSLyZbcPCxGlBRdB8hMdny/VW+SBwKD2/" << std::endl; - invalidKeyFile << "ivpVJLulw2oniSIcCCcr9d+ERY4XrO71UsiACwPxfdEtbG0KrZfpK91k7vl64DHM" << std::endl; - invalidKeyFile << "CeTQPKRZm+LDKOUfv/eTF9F6GY4Dpw2LMwLM5QIDAQABAoIBABjV91etzK+Mxa61" << std::endl; - invalidKeyFile << "AVCWzaUEkhvPvhKKGmy/VulnTj7IO98JBYlNLeoIRBIMql4QKRQWDNMMCtDQ8W6c" << std::endl; - invalidKeyFile << "Gv5kux7QvrMfYViBGQ9/gucN/pnZ+vgkrw4AuiQM8pZuZpJJ6vH9HfvC6iwQkTR+" << std::endl; - invalidKeyFile << "tdIPpvecfL3djCuTz7ns66iKo9ZGpRE6emTBynr8og/oqD8Vw5bW+JJ+AJ3IqZf4" << std::endl; - invalidKeyFile << "NslNist7d5FZ5N/+nxWyBUcFglP7bZzb/raOVc/flrYIeDy72asnWOYbDTPzMyH1" << std::endl; - invalidKeyFile << "dfaox6QKZtA5NdO9x4aHHGgAz8BTgqs7LvxPwoH+XF1dDCsb3kIeQxHTfcc1opMw" << std::endl; - invalidKeyFile << "atxpgwECgYEA8Zq/7Z3tKcBlMz4XNKWWvaDxhBUIS62tGeLJ2spLRFvkL1ixnjcK" << std::endl; - invalidKeyFile << "72YWOwDpoINEWa8AhAhM6afE9VxrupSGg+C9uALaJ8HTWTP6u6/F8sbsYaoWHyA/" << std::endl; - invalidKeyFile << "k/8/nFEr43ciKUjBhMHB42vYidAgiOvDVXc+/k7HIMQfl/vyp32ecEECgYEA5fu9" << std::endl; - invalidKeyFile << "ePLh55TYbXe8SCL0hsZcC8Q/ioT/0GJ6uevGb0lw3XAa+HC6//upu90T7ZOIqysc" << std::endl; - invalidKeyFile << "aAqln7ZEeCfvXI/3YJyJ2RWatD+2itECbd0WV2/JflO/OAzDSSFvpxxmwIzccIeA" << std::endl; - invalidKeyFile << "UNuNcQGD8HDwFzU+sULvF82yuwMt1syPd/mns6UCgYAviqP5vfnNHW7MhotKcMsY" << std::endl; - invalidKeyFile << "xXLA6uKXAbXuQhI2W1g0O2DLcEiDOZGNSilVsvhF/Y6VlzoiwP9hewHmxijsrg1K" << std::endl; - invalidKeyFile << "Jg8vBmCnMhzEkNXl2NC61SnujemMdmwMU03RFKfuOqMePJLX7MiaV75kX/AHAV2O" << std::endl; - invalidKeyFile << "k8hxgk7sw6rz3UACdVWYAQKBgHUu5ScoksS+Cd0VQmF7Nh8qGSKBt2KsS/BxDVmI" << std::endl; - invalidKeyFile << "ck6oHBMomQV340CliaHIjuvh3aRhzhKRQjzz0UVsC8GdNY4LlQ2AvZgUUr2+q78x" << std::endl; - invalidKeyFile << "BL4+nmt43pj/n822dL6wcQaxf2zzDgWlKReojwLHeP5KSgxmL49wZx51CzlEd+HI" << std::endl; - invalidKeyFile << "2pNlAoGBAObdC7woN7jEfdfYz1BhUpmBsIRqW2yLA1DnlK9lfgs2i1w7spzAh2hV" << std::endl; - invalidKeyFile << "djPiKj5vZdcrbaa+SBAnZbFTHyXmAbKbO/iZpSromaZYyCK8NktJu/YxpWZmjnRF" << std::endl; - invalidKeyFile << "2xOadRGCav5fTGzCN/ADLgIo4gIAI2o/UnV/MdaSAdHyIeSrxBAb" << std::endl; - invalidKeyFile << "-----END RSA PRIVATE KEY-----" << std::endl; - invalidKeyFile.close (); - } - } - - /** - * @brief tear down test case. - */ - static void TearDownTestCase () - { - unlink (_root.c_str ()); - unlink (_cert.c_str ()); - unlink (_key.c_str ()); - unlink (_invalidKey.c_str ()); - } - -protected: - /// host ip address. - static const IpAddress _hostip; - - /// port. - static const uint16_t _port; - - /// root certificate. - static const std::string _root; - - /// certificate. - static const std::string _cert; - - /// private key. - static const std::string _key; - - /// invalid private key. - static const std::string _invalidKey; -}; - -const IpAddress TlsAcceptor::_hostip = "::1"; -const uint16_t TlsAcceptor::_port = 5000; -const std::string TlsAcceptor::_root = "/tmp/tlsserver_test_root.cert"; -const std::string TlsAcceptor::_cert = "/tmp/tlsserver_test.cert"; -const std::string TlsAcceptor::_key = "/tmp/tlsserver_test.key"; -const std::string TlsAcceptor::_invalidKey = "/tmp/tlsserver_test_invalid.key"; - -/** - * @brief Assign by move. - */ -TEST_F (TlsAcceptor, move) -{ - Tls::Acceptor server1, server2; - - ASSERT_EQ (server1.create ({_hostip, _port}), 0) << join::lastError.message (); - - server2 = std::move (server1); - ASSERT_TRUE (server2.opened ()); - - Tls::Acceptor server3 = std::move (server2); - ASSERT_TRUE (server3.opened ()); -} - -/** - * @brief Test create method. - */ -TEST_F (TlsAcceptor, create) -{ - Tls::Acceptor server1, server2; - - ASSERT_EQ (server1.create ({_hostip, _port}), 0) << join::lastError.message (); - - ASSERT_EQ (server1.create ({_hostip, _port}), -1); - ASSERT_EQ (join::lastError, Errc::InUse); - - ASSERT_EQ (server2.create ({_hostip, _port}), -1); - ASSERT_EQ (join::lastError, Errc::InUse); -} - -/** - * @brief Test close method. - */ -TEST_F (TlsAcceptor, close) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (server.opened ()); - server.close (); - ASSERT_FALSE (server.opened ()); -} - -/** - * @brief Test accept method. - */ -TEST_F (TlsAcceptor, accept) -{ - Tls::Socket clientSocket (Tls::Socket::Blocking); - Tls::Acceptor server; - - ASSERT_FALSE (server.accept ().connected ()); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_EQ (clientSocket.connect ({_hostip, _port}), 0) << join::lastError.message (); - Tls::Socket serverSocket = server.accept (); - ASSERT_TRUE (serverSocket.connected ()); - ASSERT_FALSE (serverSocket.encrypted ()); - ASSERT_EQ (serverSocket.localEndpoint ().ip (), _hostip); - ASSERT_EQ (serverSocket.localEndpoint ().port (), _port); -} - -/** - * @brief Test acceptStream method. - */ -TEST_F (TlsAcceptor, acceptStream) -{ - Tls::Socket clientSocket (Tls::Socket::Blocking); - Tls::Acceptor server; - - ASSERT_FALSE (server.acceptStream ().connected ()); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_EQ (clientSocket.connect ({_hostip, _port}), 0) << join::lastError.message (); - Tls::Stream serverStream = server.acceptStream (); - ASSERT_TRUE (serverStream.connected ()); - ASSERT_FALSE (serverStream.encrypted ()); - ASSERT_EQ (serverStream.socket ().localEndpoint ().ip (), _hostip); - ASSERT_EQ (serverStream.socket ().localEndpoint ().port (), _port); -} - -/** - * @brief Test acceptEncrypted method. - */ -TEST_F (TlsAcceptor, acceptEncrypted) -{ - Tls::Socket clientSocket (Tls::Socket::Blocking); - Tls::Acceptor server; - - ASSERT_FALSE (server.acceptEncrypted ().connected ()); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_EQ (clientSocket.connect ({_hostip, _port}), 0) << join::lastError.message (); - Tls::Socket serverSocket = server.acceptEncrypted (); - ASSERT_TRUE (serverSocket.connected ()); - ASSERT_TRUE (serverSocket.encrypted ()); - ASSERT_EQ (serverSocket.localEndpoint ().ip (), _hostip); - ASSERT_EQ (serverSocket.localEndpoint ().port (), _port); -} - -/** - * @brief Test acceptStreamEncrypted method. - */ -TEST_F (TlsAcceptor, acceptStreamEncrypted) -{ - Tls::Socket clientSocket (Tls::Socket::Blocking); - Tls::Acceptor server; - - ASSERT_FALSE (server.acceptStreamEncrypted ().connected ()); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_EQ (clientSocket.connect ({_hostip, _port}), 0) << join::lastError.message (); - Tls::Stream serverStream = server.acceptStreamEncrypted (); - ASSERT_TRUE (serverStream.connected ()); - ASSERT_TRUE (serverStream.encrypted ()); - ASSERT_EQ (serverStream.socket ().localEndpoint ().ip (), _hostip); - ASSERT_EQ (serverStream.socket ().localEndpoint ().port (), _port); -} - -/** - * @brief Test localEndpoint method. - */ -TEST_F (TlsAcceptor, localEndpoint) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.localEndpoint (), Tls::Endpoint{}); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_EQ (server.localEndpoint ().ip (), _hostip); - ASSERT_EQ (server.localEndpoint ().port (), _port); -} - -/** - * @brief Test opened method. - */ -TEST_F (TlsAcceptor, opened) -{ - Tls::Acceptor server; - - ASSERT_FALSE (server.opened ()); - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (server.opened ()); - server.close (); - ASSERT_FALSE (server.opened ()); -} - -/** - * @brief Test family method. - */ -TEST_F (TlsAcceptor, family) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_EQ (server.family (), _hostip.family ()); -} - -/** - * @brief Test type method. - */ -TEST_F (TlsAcceptor, type) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_EQ (server.type (), SOCK_STREAM); -} - -/** - * @brief Test protocol method. - */ -TEST_F (TlsAcceptor, protocol) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_EQ (server.protocol (), IPPROTO_TCP); -} - -/** - * @brief Test handle method. - */ -TEST_F (TlsAcceptor, handle) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.handle (), -1); - ASSERT_EQ (server.create ({_hostip, _port}), 0) << join::lastError.message (); - ASSERT_GT (server.handle (), -1); - server.close (); - ASSERT_EQ (server.handle (), -1); -} - -/** - * @brief Test setCertificate method. - */ -TEST_F (TlsAcceptor, setCertificate) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.setCertificate ("foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (server.setCertificate (_cert), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (server.setCertificate (_cert, "foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (server.setCertificate (_cert, _invalidKey), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (server.setCertificate (_cert, _key), 0) << join::lastError.message (); -} - -/** - * @brief Test setVerify method. - */ -TEST_F (TlsAcceptor, setVerify) -{ - Tls::Acceptor server; - - ASSERT_NO_THROW (server.setVerify (true)); - ASSERT_NO_THROW (server.setVerify (false)); -} - -/** - * @brief Test setCaCertificate method. - */ -TEST_F (TlsAcceptor, setCaCertificate) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.setCaCertificate ("foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (server.setCaCertificate (_cert), 0) << join::lastError.message (); -} - -/** - * @brief Test setCipher method. - */ -TEST_F (TlsAcceptor, setCipher) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.setCipher ("foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (server.setCipher (join::defaultCipher), 0) << join::lastError.message (); -} - -/** - * @brief Test setCipher_1_3 method. - */ -TEST_F (TlsAcceptor, setCipher_1_3) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.setCipher_1_3 ("foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (server.setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); -} - -#if OPENSSL_VERSION_NUMBER >= 0x30000000L -/** - * @brief Test setCurve method. - */ -TEST_F (TlsAcceptor, setCurve) -{ - Tls::Acceptor server; - - ASSERT_EQ (server.setCurve ("foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (server.setCurve (join::defaultCurve), 0) << join::lastError.message (); -} -#endif - -/** - * @brief main function. - */ -int main (int argc, char** argv) -{ - join::initializeOpenSSL (); - testing::InitGoogleTest (&argc, argv); - return RUN_ALL_TESTS (); -} diff --git a/core/tests/tlserror_test.cpp b/core/tests/tlserror_test.cpp deleted file mode 100644 index d1eeff07..00000000 --- a/core/tests/tlserror_test.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 Mathieu Rabine - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// libjoin. -#include - -// Libraries. -#include - -using join::TlsErrc; -using join::TlsCategory; - -/** - * @brief Test name. - */ -TEST (TlsCategory, name) -{ - EXPECT_STREQ (TlsCategory ().name (), "libjoin"); -} - -/** - * @brief Test message. - */ -TEST (TlsCategory, message) -{ - EXPECT_STREQ (TlsCategory ().message (0).c_str (), "success"); - EXPECT_STREQ (TlsCategory ().message (static_cast (TlsErrc::TlsCloseNotifyAlert)).c_str (), - "TLS close notify alert received"); - EXPECT_STREQ (TlsCategory ().message (static_cast (TlsErrc::TlsProtocolError)).c_str (), "TLS protocol error"); -} - -/** - * @brief Test default_error_condition. - */ -TEST (TlsCategory, default_error_condition) -{ - EXPECT_EQ (TlsCategory ().default_error_condition (0).message (), "success"); - EXPECT_EQ (TlsCategory ().default_error_condition (1).message (), "TLS close notify alert received"); - EXPECT_EQ (TlsCategory ().default_error_condition (2).message (), "TLS protocol error"); -} - -/** - * @brief Test equal. - */ -TEST (TlsCategory, equal) -{ - std::error_code error1, error2; - - error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - error2 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - ASSERT_TRUE (error1 == error2); - - error2 = make_error_code (TlsErrc::TlsProtocolError); - ASSERT_FALSE (error1 == error2); - - error1 = make_error_code (TlsErrc::TlsProtocolError); - ASSERT_TRUE (error1 == error2); - - error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - ASSERT_TRUE (error1 == TlsErrc::TlsCloseNotifyAlert); - - error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - ASSERT_FALSE (error1 == TlsErrc::TlsProtocolError); - - error2 = make_error_code (TlsErrc::TlsProtocolError); - ASSERT_TRUE (TlsErrc::TlsProtocolError == error2); - - error2 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - ASSERT_FALSE (TlsErrc::TlsProtocolError == error2); -} - -/** - * @brief Test different. - */ -TEST (TlsCategory, different) -{ - std::error_code error1, error2; - - error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - error2 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - ASSERT_FALSE (error1 != error2); - - error2 = make_error_code (TlsErrc::TlsProtocolError); - ASSERT_TRUE (error1 != error2); - - error1 = make_error_code (TlsErrc::TlsProtocolError); - ASSERT_FALSE (error1 != error2); - - error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - ASSERT_FALSE (error1 != TlsErrc::TlsCloseNotifyAlert); - - error1 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - ASSERT_TRUE (error1 != TlsErrc::TlsProtocolError); - - error2 = make_error_code (TlsErrc::TlsProtocolError); - ASSERT_FALSE (TlsErrc::TlsProtocolError != error2); - - error2 = make_error_code (TlsErrc::TlsCloseNotifyAlert); - ASSERT_TRUE (TlsErrc::TlsProtocolError != error2); -} - -/** - * @brief Test make_error_code. - */ -TEST (TlsCategory, make_error_code) -{ - auto code = make_error_code (TlsErrc::TlsCloseNotifyAlert); - EXPECT_EQ (code, TlsErrc::TlsCloseNotifyAlert); - EXPECT_STREQ (code.message ().c_str (), "TLS close notify alert received"); -} - -/** - * @brief Test make_error_condition. - */ -TEST (TlsCategory, make_error_condition) -{ - auto code = make_error_condition (TlsErrc::TlsProtocolError); - EXPECT_EQ (code, TlsErrc::TlsProtocolError); - EXPECT_STREQ (code.message ().c_str (), "TLS protocol error"); -} - -/** - * @brief main function. - */ -int main (int argc, char** argv) -{ - testing::InitGoogleTest (&argc, argv); - return RUN_ALL_TESTS (); -} diff --git a/core/tests/tlssocket_test.cpp b/core/tests/tlssocket_test.cpp deleted file mode 100644 index 9b329d18..00000000 --- a/core/tests/tlssocket_test.cpp +++ /dev/null @@ -1,1085 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 Mathieu Rabine - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// libjoin. -#include -#include - -// Libraries. -#include - -// C++. -#include - -using join::Errc; -using join::IpAddress; -using join::ReactorThread; -using join::EventHandler; -using join::Tls; - -/** - * @brief Class used to test the TLS socket API. - */ -class TlsSocket : public Tls::Acceptor, public EventHandler, public ::testing::Test -{ -public: - /** - * @brief set up test case. - */ - static void SetUpTestCase () - { - std::ofstream rootCertFile (_rootcert); - if (rootCertFile.is_open ()) - { - rootCertFile << "-----BEGIN CERTIFICATE-----" << std::endl; - rootCertFile << "MIIChjCCAisCFBuHxbqMUGyl7OQUQcoRg3pOBJF+MAoGCCqGSM49BAMCMIHEMQsw" << std::endl; - rootCertFile << "CQYDVQQGEwJGUjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVz" << std::endl; - rootCertFile << "MRcwFQYDVQQKDA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdv" << std::endl; - rootCertFile << "cmsgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3" << std::endl; - rootCertFile << "b3JrLm5ldDEoMCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5l" << std::endl; - rootCertFile << "dDAeFw0yMjA3MDUxNjMxMTZaFw0zMjA3MDIxNjMxMTZaMIHEMQswCQYDVQQGEwJG" << std::endl; - rootCertFile << "UjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVzMRcwFQYDVQQK" << std::endl; - rootCertFile << "DA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdvcmsgQ2VydGlm" << std::endl; - rootCertFile << "aWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3b3JrLm5ldDEo" << std::endl; - rootCertFile << "MCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5ldDBZMBMGByqG" << std::endl; - rootCertFile << "SM49AgEGCCqGSM49AwEHA0IABASk0zCrKtXQi0Ycx+Anx+VWv8gncbPmNQ1yutii" << std::endl; - rootCertFile << "gQjP2mF9NIqlxpcKNuE/6DDnfSzCEDhFyvGiK0NJ1C3RBowwCgYIKoZIzj0EAwID" << std::endl; - rootCertFile << "SQAwRgIhAIFqdbxTb5kRjy4UY0N205ZEhHSMK89p2oUyn4iNbXH2AiEAtmV1UyRX" << std::endl; - rootCertFile << "DIAGr/F+1SwQMPoJzSQxZ7NdxjNgW286e9Q=" << std::endl; - rootCertFile << "-----END CERTIFICATE-----" << std::endl; - rootCertFile.close (); - } - - mkdir (_certPath.c_str (), 0777); - std::ofstream certFile (_certFile); - if (certFile.is_open ()) - { - certFile << "-----BEGIN CERTIFICATE-----" << std::endl; - certFile << "MIIDgDCCAyagAwIBAgIUR3ZIuKMt0BdaOZQnPwhSMR9qzfgwCgYIKoZIzj0EAwIw" << std::endl; - certFile << "gcQxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nh" << std::endl; - certFile << "c3RyZXMxFzAVBgNVBAoMDkpvaW4gRnJhbWV3b3JrMS0wKwYDVQQLDCRKb2luIEZy" << std::endl; - certFile << "YW1ld29yayBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHTAbBgNVBAMMFGNhLmpvaW5m" << std::endl; - certFile << "cmFtZXdvcmsubmV0MSgwJgYJKoZIhvcNAQkBFhlzdXBwb3J0QGpvaW5mcmFtZXdv" << std::endl; - certFile << "cmsubmV0MB4XDTIyMDcwNzEyMTIxMFoXDTMyMDcwNDEyMTIxMFowgagxCzAJBgNV" << std::endl; - certFile << "BAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nhc3RyZXMxFzAV" << std::endl; - certFile << "BgNVBAoMDkpvaW4gRnJhbWV3b3JrMRswGQYDVQQLDBJKb2luIEZyYW1ld29yayBE" << std::endl; - certFile << "ZXYxEzARBgNVBAMMCmxvY2FsaG9zdC4xKDAmBgkqhkiG9w0BCQEWGXN1cHBvcnRA" << std::endl; - certFile << "am9pbmZyYW1ld29yay5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" << std::endl; - certFile << "AQDSNtw5zEoJFPf6Rl0Y1n8BQfE0YTPCELvFAeioUfj8CAnUleHL9pwAEFg6kgoG" << std::endl; - certFile << "hvwto5/yWGPUqNNfe3xbFTJcHgMhgtjqy5H6sYDkTi3kYIIMBfTHr8NI7HWE8Nz1" << std::endl; - certFile << "qU1snjtERnkoLilIZf/2BojNVMtHC1H316WbMicXS0v7HQo3lv6PYSana9Q9ow9O" << std::endl; - certFile << "2/FiW5qq1eOhI1ZedRanX+bl0jHWCd3WsI87+5bTaQrfetdHTOmav6O17Iq9FiTh" << std::endl; - certFile << "Sg9fbM3s2Hw15kI+mws029dhcwXs5sYY+NgtrQwjR5qH+54BdUaPwQfl/KyulfEl" << std::endl; - certFile << "TJykJ+3w6MorxUr55F68uBNbAgMBAAGjRTBDMAsGA1UdDwQEAwIF4DAdBgNVHSUE" << std::endl; - certFile << "FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwFQYDVR0RBA4wDIIKbG9jYWxob3N0LjAK" << std::endl; - certFile << "BggqhkjOPQQDAgNIADBFAiA120ufIbhcw7BJQ1L6WudDdW2mHrVXvdgeOzVGgz1d" << std::endl; - certFile << "iAIhAMm/sWI3yzb2IMPffxWKYusWEQE2hZvs24ESSC/ZZ0s+" << std::endl; - certFile << "-----END CERTIFICATE-----" << std::endl; - certFile.close (); - } - - [[maybe_unused]] int result; - result = std::system (std::string ("/usr/bin/c_rehash " + _certPath).c_str ()); - - std::ofstream keyFile (_key); - if (keyFile.is_open ()) - { - keyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; - keyFile << "MIIEowIBAAKCAQEA0jbcOcxKCRT3+kZdGNZ/AUHxNGEzwhC7xQHoqFH4/AgJ1JXh" << std::endl; - keyFile << "y/acABBYOpIKBob8LaOf8lhj1KjTX3t8WxUyXB4DIYLY6suR+rGA5E4t5GCCDAX0" << std::endl; - keyFile << "x6/DSOx1hPDc9alNbJ47REZ5KC4pSGX/9gaIzVTLRwtR99elmzInF0tL+x0KN5b+" << std::endl; - keyFile << "j2Emp2vUPaMPTtvxYluaqtXjoSNWXnUWp1/m5dIx1gnd1rCPO/uW02kK33rXR0zp" << std::endl; - keyFile << "mr+jteyKvRYk4UoPX2zN7Nh8NeZCPpsLNNvXYXMF7ObGGPjYLa0MI0eah/ueAXVG" << std::endl; - keyFile << "j8EH5fysrpXxJUycpCft8OjKK8VK+eRevLgTWwIDAQABAoIBAAzdlK7o5OMXaHHl" << std::endl; - keyFile << "2o7Jme5Oxd9pz4wiEAvnqQCcO7vZFhjvr2kXR8btOSkkhP6PRmHYsNJZPIroZj9i" << std::endl; - keyFile << "xGKisnlW0OQ9KN995ApO0M+oRUDD81GfD7Mk+7O73Rls0GksmnN6X7A3C/U8lgQ7" << std::endl; - keyFile << "UeYR0k+Wz/YiKDsd9KHB+QiA8D6HFQ9I8Y2P97KOcYnxXZfSwNm+ENNU3wShZOl2" << std::endl; - keyFile << "ZYJJ4DE+5m2SwZ6g8b5Zre4cDbOduwuz/jXzjy2tAZBlTS4DVpYlhd14z+ssUWiu" << std::endl; - keyFile << "AdS/nqSF7Obj0TRhoGNfrkisFzV4itavQ5DKGj/6hjueIJVLteUOzcCeg26YosNy" << std::endl; - keyFile << "QzZSjOECgYEA7y3InEoh93/4HZCZmdwN8KfZtqirX0t966FntgAT8RkIs+KvNS8B" << std::endl; - keyFile << "m3RfNLa/EuDt5zTmHRGx+oeN+17i9QQjKWcR0NnJ6aSZbvJByj3yKxLF9XVllzp/" << std::endl; - keyFile << "vHSSyB264RoKIrWmFN6cCO4u4h9ZPY75pASWBCDMdnGK8axAcqAnlqsCgYEA4P+Y" << std::endl; - keyFile << "FF9RW4rhrVU4dpXSfcr6vOwqfp9F9vhTVL0JS/SLOFoJNNpS9Rnq3pVLEuKyCphd" << std::endl; - keyFile << "3nk9VFfoRygmMaGBvwGaXZPPvosoaIUgOdTt7KIfSHPichBEVxRuWCrtTGGkG0ok" << std::endl; - keyFile << "s/RPHhvxZE267vsVj1PktK8Yr5Ba0AL2ycztNhECgYB5OAwHYe8LIBlg6otelk+e" << std::endl; - keyFile << "W4OU9rE8L+eWx4vniuyQce6eNNI1syguYHFsJv56E/OfDYlezDwWzCLidnmyUjF7" << std::endl; - keyFile << "51f5MJgLyTdWKoO7e1/EAtS/jYs6dRSOL8rAj4jKU0c1xjhxNU2BnS23vsmc0Fyn" << std::endl; - keyFile << "iwd4+iKGGQ+hYnqbXZ4S1wKBgD/3an0gPDkSWua0e8D7B0TMGEztt4cYMQPtxYMp" << std::endl; - keyFile << "2yLE+2+h6UwlZcBZBfUR7K4J1SQ9/THqtgzskRTpzTH/AKwVAJXqF/3MAkj00Byg" << std::endl; - keyFile << "9KN50/r9NzvGdCdtn5FhYuV8PPOlOJoQsw2UVCR4FNUsfQyqhTL5NMN0/tx0e0UU" << std::endl; - keyFile << "BbyBAoGBANu5ifByauVELH8UEl5rXRu1S9iAVV+Bc5jboXwc4VxJtEyomGJ7+YdL" << std::endl; - keyFile << "5c9LFV+STUp7CE12uSXQZTQM0tEjPinLntRinNzu9tIHR1vy7FZHEwMFIgB4VTY7" << std::endl; - keyFile << "ALRYv1/QpTuywpNUFRS15JkfGNf5JIkrUEWLgkX3OVCBsRGHUugy" << std::endl; - keyFile << "-----END RSA PRIVATE KEY-----" << std::endl; - keyFile.close (); - } - - std::ofstream invalidKeyFile (_invalidKey); - if (invalidKeyFile.is_open ()) - { - invalidKeyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; - invalidKeyFile << "MIIEowIBAAKCAQEA2Q0DOyG039uVMuxNnZ5fpfOcvXXOTguST1QR6eLVkdG7OKpM" << std::endl; - invalidKeyFile << "nc9K597jx1syT1q+SwFcykMtvWxCfD8BR7bcLILeO6z+HlRfvjOhUiHaX/KCaTN8" << std::endl; - invalidKeyFile << "l7OJOgmUlL0FhQ1SXxw7KCSGd+rgu1iHwjFDDkj/tG24ashdmNt+DYdeoJu2mzgw" << std::endl; - invalidKeyFile << "tEASfG9VjqBR7ni4Hg/sRpwXvEK5nI1JSLyZbcPCxGlBRdB8hMdny/VW+SBwKD2/" << std::endl; - invalidKeyFile << "ivpVJLulw2oniSIcCCcr9d+ERY4XrO71UsiACwPxfdEtbG0KrZfpK91k7vl64DHM" << std::endl; - invalidKeyFile << "CeTQPKRZm+LDKOUfv/eTF9F6GY4Dpw2LMwLM5QIDAQABAoIBABjV91etzK+Mxa61" << std::endl; - invalidKeyFile << "AVCWzaUEkhvPvhKKGmy/VulnTj7IO98JBYlNLeoIRBIMql4QKRQWDNMMCtDQ8W6c" << std::endl; - invalidKeyFile << "Gv5kux7QvrMfYViBGQ9/gucN/pnZ+vgkrw4AuiQM8pZuZpJJ6vH9HfvC6iwQkTR+" << std::endl; - invalidKeyFile << "tdIPpvecfL3djCuTz7ns66iKo9ZGpRE6emTBynr8og/oqD8Vw5bW+JJ+AJ3IqZf4" << std::endl; - invalidKeyFile << "NslNist7d5FZ5N/+nxWyBUcFglP7bZzb/raOVc/flrYIeDy72asnWOYbDTPzMyH1" << std::endl; - invalidKeyFile << "dfaox6QKZtA5NdO9x4aHHGgAz8BTgqs7LvxPwoH+XF1dDCsb3kIeQxHTfcc1opMw" << std::endl; - invalidKeyFile << "atxpgwECgYEA8Zq/7Z3tKcBlMz4XNKWWvaDxhBUIS62tGeLJ2spLRFvkL1ixnjcK" << std::endl; - invalidKeyFile << "72YWOwDpoINEWa8AhAhM6afE9VxrupSGg+C9uALaJ8HTWTP6u6/F8sbsYaoWHyA/" << std::endl; - invalidKeyFile << "k/8/nFEr43ciKUjBhMHB42vYidAgiOvDVXc+/k7HIMQfl/vyp32ecEECgYEA5fu9" << std::endl; - invalidKeyFile << "ePLh55TYbXe8SCL0hsZcC8Q/ioT/0GJ6uevGb0lw3XAa+HC6//upu90T7ZOIqysc" << std::endl; - invalidKeyFile << "aAqln7ZEeCfvXI/3YJyJ2RWatD+2itECbd0WV2/JflO/OAzDSSFvpxxmwIzccIeA" << std::endl; - invalidKeyFile << "UNuNcQGD8HDwFzU+sULvF82yuwMt1syPd/mns6UCgYAviqP5vfnNHW7MhotKcMsY" << std::endl; - invalidKeyFile << "xXLA6uKXAbXuQhI2W1g0O2DLcEiDOZGNSilVsvhF/Y6VlzoiwP9hewHmxijsrg1K" << std::endl; - invalidKeyFile << "Jg8vBmCnMhzEkNXl2NC61SnujemMdmwMU03RFKfuOqMePJLX7MiaV75kX/AHAV2O" << std::endl; - invalidKeyFile << "k8hxgk7sw6rz3UACdVWYAQKBgHUu5ScoksS+Cd0VQmF7Nh8qGSKBt2KsS/BxDVmI" << std::endl; - invalidKeyFile << "ck6oHBMomQV340CliaHIjuvh3aRhzhKRQjzz0UVsC8GdNY4LlQ2AvZgUUr2+q78x" << std::endl; - invalidKeyFile << "BL4+nmt43pj/n822dL6wcQaxf2zzDgWlKReojwLHeP5KSgxmL49wZx51CzlEd+HI" << std::endl; - invalidKeyFile << "2pNlAoGBAObdC7woN7jEfdfYz1BhUpmBsIRqW2yLA1DnlK9lfgs2i1w7spzAh2hV" << std::endl; - invalidKeyFile << "djPiKj5vZdcrbaa+SBAnZbFTHyXmAbKbO/iZpSromaZYyCK8NktJu/YxpWZmjnRF" << std::endl; - invalidKeyFile << "2xOadRGCav5fTGzCN/ADLgIo4gIAI2o/UnV/MdaSAdHyIeSrxBAb" << std::endl; - invalidKeyFile << "-----END RSA PRIVATE KEY-----" << std::endl; - invalidKeyFile.close (); - } - } - - /** - * @brief tear down test case. - */ - static void TearDownTestCase () - { - unlink (_rootcert.c_str ()); - unlink (_certFile.c_str ()); - rmdir (_certPath.c_str ()); - unlink (_key.c_str ()); - unlink (_invalidKey.c_str ()); - } - -protected: - /** - * @brief Sets up the test fixture. - */ - void SetUp () override - { - ASSERT_EQ (this->setCertificate (_certFile, _key), 0) << join::lastError.message (); - ASSERT_EQ (this->setCipher (join::defaultCipher), 0) << join::lastError.message (); - ASSERT_EQ (this->setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); - ASSERT_EQ (this->create ({IpAddress::ipv6Wildcard, _port}), 0) << join::lastError.message (); - ASSERT_EQ (ReactorThread::reactor ().addHandler (handle (), this), 0) << join::lastError.message (); - } - - /** - * @brief Tears down the test fixture. - */ - void TearDown () override - { - ASSERT_EQ (ReactorThread::reactor ().delHandler (handle ()), 0) << join::lastError.message (); - this->close (); - } - - /** - * @brief method called when data are ready to be read on handle. - * @param fd file descriptor. - */ - virtual void onReadable ([[maybe_unused]] int fd) override - { - Tls::Socket sock = this->acceptEncrypted (); - if (sock.connected ()) - { - char buf[1024]; - for (;;) - { - // echo received data. - int nread = sock.read (buf, sizeof (buf)); - if (nread == -1) - { - if (join::lastError == Errc::TemporaryError) - { - if (sock.waitReadyRead (_timeout)) - continue; - } - break; - } - sock.writeExactly (buf, nread); - } - sock.close (); - } - } - - /// host. - static const std::string _hostv4; - static const std::string _hostv6; - - /// port. - static const uint16_t _port; - static const uint16_t _invalid_port; - - /// timeout. - static const int _timeout; - - /// root certificate. - static const std::string _rootcert; - - /// certificate path. - static const std::string _certPath; - - /// certificate file. - static const std::string _certFile; - - /// private key. - static const std::string _key; - - /// invalid private key. - static const std::string _invalidKey; -}; - -const std::string TlsSocket::_hostv4 = "127.0.0.1"; -const std::string TlsSocket::_hostv6 = "::1"; -const uint16_t TlsSocket::_port = 5000; -const uint16_t TlsSocket::_invalid_port = 5032; -const int TlsSocket::_timeout = 1000; -const std::string TlsSocket::_rootcert = "/tmp/tlssocket_test_root.cert"; -const std::string TlsSocket::_certPath = "/tmp/certs"; -const std::string TlsSocket::_certFile = _certPath + "/tlssocket_test.cert"; -const std::string TlsSocket::_key = "/tmp/tlssocket_test.key"; -const std::string TlsSocket::_invalidKey = "/tmp/tlssocket_test_invalid.key"; - -/** - * @brief Test construct method. - */ -TEST_F (TlsSocket, construct) -{ - ASSERT_THROW (Tls::Socket (nullptr), std::invalid_argument); -} - -/** - * @brief Test move. - */ -TEST_F (TlsSocket, move) -{ - Tls::Socket tlsSocket1 (Tls::Socket::Blocking), tlsSocket2; - - ASSERT_EQ (tlsSocket1.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket1.connected ()); - tlsSocket2 = std::move (tlsSocket1); - ASSERT_TRUE (tlsSocket2.connected ()); - tlsSocket2.close (); -} - -/** - * @brief Test open method. - */ -TEST_F (TlsSocket, open) -{ - Tls::Socket tlsSocket; - - ASSERT_EQ (tlsSocket.open (Tls::v4 ()), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.open (Tls::v4 ()), -1); - ASSERT_EQ (join::lastError, Errc::InUse); - tlsSocket.close (); - - ASSERT_EQ (tlsSocket.open (Tls::v6 ()), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.open (Tls::v6 ()), -1); - ASSERT_EQ (join::lastError, Errc::InUse); - tlsSocket.close (); -} - -/** - * @brief Test close method. - */ -TEST_F (TlsSocket, close) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_FALSE (tlsSocket.opened ()); - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.opened ()); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.opened ()); - tlsSocket.close (); - ASSERT_FALSE (tlsSocket.opened ()); -} - -/** - * @brief Test bind method. - */ -TEST_F (TlsSocket, bind) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.bind (_hostv4), -1); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); - - ASSERT_EQ (tlsSocket.bind (_hostv4), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test bindToDevice method. - */ -TEST_F (TlsSocket, bindToDevice) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.bindToDevice ("lo"), -1); - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.bindToDevice ("lo"), -1); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); - - ASSERT_EQ (tlsSocket.open (Tls::v6 ()), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.bindToDevice ("lo"), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connect ({_hostv6, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.bindToDevice ("foo"), -1); - tlsSocket.close (); -} - -/** - * @brief Test connect method. - */ -TEST_F (TlsSocket, connect) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.connect ({"255.255.255.255", _port}), -1); - - ASSERT_EQ (tlsSocket.connect ({_hostv4, _invalid_port}), -1); - - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), -1); - ASSERT_EQ (join::lastError, Errc::InUse); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test waitConnected method. - */ -TEST_F (TlsSocket, waitConnected) -{ - Tls::Socket tlsSocket; - - ASSERT_FALSE (tlsSocket.waitConnected (_timeout)); - if (tlsSocket.connect ({_hostv4, _port}) == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.connecting ()); - } - ASSERT_TRUE (tlsSocket.waitConnected (_timeout)) << join::lastError.message (); - if (tlsSocket.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitDisconnected (_timeout)) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test connectEncrypted method. - */ -TEST_F (TlsSocket, connectEncrypted) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.connectEncrypted ({"255.255.255.255", _port}), -1); - - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _invalid_port}), -1); - - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test startEncryption method. - */ -TEST_F (TlsSocket, startEncryption) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.startEncryption (), -1); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.startEncryption (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.startEncryption (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test waitEncrypted method. - */ -TEST_F (TlsSocket, waitEncrypted) -{ - Tls::Socket tlsSocket (Tls::Socket::NonBlocking); - - ASSERT_EQ (tlsSocket.open (Tls::v6 ()), 0) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.waitEncrypted (_timeout)); - if (tlsSocket.connect ({_hostv6, _port}) == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitConnected (_timeout)) << join::lastError.message (); - if (tlsSocket.startEncryption () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitEncrypted (_timeout)) << join::lastError.message (); - if (tlsSocket.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitDisconnected (_timeout)) << join::lastError.message (); - tlsSocket.close (); - - if (tlsSocket.connectEncrypted ({_hostv6, _port}) == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitEncrypted (_timeout)) << join::lastError.message (); - if (tlsSocket.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitDisconnected (_timeout)) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test disconnect method. - */ -TEST_F (TlsSocket, disconnect) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_FALSE (tlsSocket.connected ()); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.connected ()); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.connected ()); - tlsSocket.close (); - ASSERT_FALSE (tlsSocket.connected ()); -} - -/** - * @brief Test waitDisconnected method. - */ -TEST_F (TlsSocket, waitDisconnected) -{ - Tls::Socket tlsSocket (Tls::Socket::NonBlocking); - - ASSERT_TRUE (tlsSocket.waitDisconnected (_timeout)) << join::lastError.message (); - if (tlsSocket.connectEncrypted ({_hostv4, _port}) == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitEncrypted (_timeout)) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.waitDisconnected (_timeout)); - if (tlsSocket.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitDisconnected (_timeout)) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test waitReadyRead method. - */ -TEST_F (TlsSocket, waitReadyRead) -{ - Tls::Socket tlsSocket; - char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; - - ASSERT_FALSE (tlsSocket.waitReadyRead (_timeout)); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - if (tlsSocket.connectEncrypted ({_hostv4, _port}) == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitEncrypted (_timeout)) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyWrite (_timeout)) << join::lastError.message (); - ASSERT_EQ (tlsSocket.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyRead (_timeout)) << join::lastError.message (); - if (tlsSocket.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitDisconnected (_timeout)) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test canRead method. - */ -TEST_F (TlsSocket, canRead) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; - - ASSERT_EQ (tlsSocket.canRead (), -1); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyWrite (_timeout)) << join::lastError.message (); - ASSERT_EQ (tlsSocket.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyRead (_timeout)) << join::lastError.message (); - ASSERT_EQ (tlsSocket.read (data, 1), 1) << join::lastError.message (); - ASSERT_GT (tlsSocket.canRead (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test read method. - */ -TEST_F (TlsSocket, read) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; - - ASSERT_EQ (tlsSocket.read (data, sizeof (data)), -1); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyWrite (_timeout)) << join::lastError.message (); - ASSERT_EQ (tlsSocket.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyRead (_timeout)) << join::lastError.message (); - ASSERT_GT (tlsSocket.read (data, sizeof (data)), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test readExactly method. - */ -TEST_F (TlsSocket, readExactly) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; - - ASSERT_EQ (tlsSocket.readExactly (data, sizeof (data)), -1); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyWrite (_timeout)) << join::lastError.message (); - ASSERT_EQ (tlsSocket.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyRead (_timeout)) << join::lastError.message (); - ASSERT_EQ (tlsSocket.readExactly (data, sizeof (data)), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test waitReadyWrite method. - */ -TEST_F (TlsSocket, waitReadyWrite) -{ - Tls::Socket tlsSocket; - - ASSERT_FALSE (tlsSocket.waitReadyWrite (_timeout)); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - if (tlsSocket.connectEncrypted ({_hostv4, _port}) == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitEncrypted (_timeout)) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyWrite (_timeout)) << join::lastError.message (); - if (tlsSocket.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (tlsSocket.waitDisconnected (_timeout)) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test write method. - */ -TEST_F (TlsSocket, write) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; - - ASSERT_EQ (tlsSocket.write (data, sizeof (data)), -1); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyWrite (_timeout)) << join::lastError.message (); - ASSERT_GT (tlsSocket.write (data, sizeof (data)), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyRead (_timeout)) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test writeExactly method. - */ -TEST_F (TlsSocket, writeExactly) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; - - ASSERT_EQ (tlsSocket.writeExactly (data, sizeof (data)), -1); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyWrite (_timeout)) << join::lastError.message (); - ASSERT_EQ (tlsSocket.writeExactly (data, sizeof (data)), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.waitReadyRead (_timeout)) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test setMode method. - */ -TEST_F (TlsSocket, setMode) -{ - Tls::Socket tlsSocket; - - ASSERT_EQ (tlsSocket.open (), 0) << join::lastError.message (); - - int flags = ::fcntl (tlsSocket.handle (), F_GETFL, 0); - ASSERT_TRUE (flags & O_NONBLOCK); - - tlsSocket.setMode (Tls::Socket::Blocking); - flags = ::fcntl (tlsSocket.handle (), F_GETFL, 0); - ASSERT_FALSE (flags & O_NONBLOCK); - - tlsSocket.setMode (Tls::Socket::NonBlocking); - flags = ::fcntl (tlsSocket.handle (), F_GETFL, 0); - ASSERT_TRUE (flags & O_NONBLOCK); - - tlsSocket.close (); -} - -/** - * @brief Test setOption method. - */ -TEST_F (TlsSocket, setOption) -{ - Tls::Socket tlsSocket; - - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::RcvBuffer, 1500), -1); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - - ASSERT_EQ (tlsSocket.open (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::NoDelay, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::KeepAlive, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::KeepIdle, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::KeepIntvl, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::KeepCount, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::SndBuffer, 1500), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::RcvBuffer, 1500), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::TimeStamp, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::ReuseAddr, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::ReusePort, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::Broadcast, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::Ttl, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::MulticastLoop, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::MulticastTtl, 1), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::PathMtuDiscover, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::RcvError, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::AuxData, 1), -1); - ASSERT_EQ (join::lastError, std::errc::no_protocol_option); - tlsSocket.close (); - - ASSERT_EQ (tlsSocket.open (Tls::v6 ()), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::NoDelay, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::KeepAlive, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::KeepIdle, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::KeepIntvl, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::KeepCount, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::SndBuffer, 1500), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::RcvBuffer, 1500), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::TimeStamp, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::ReuseAddr, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::ReusePort, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::Broadcast, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::Ttl, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::MulticastLoop, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::MulticastTtl, 1), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::PathMtuDiscover, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::RcvError, 1), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setOption (Tls::Socket::AuxData, 1), -1); - ASSERT_EQ (join::lastError, std::errc::no_protocol_option); - tlsSocket.close (); -} - -/** - * @brief Test localEndpoint method. - */ -TEST_F (TlsSocket, localEndpoint) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.localEndpoint (), Tls::Endpoint{}); - ASSERT_EQ (tlsSocket.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.localEndpoint (), Tls::Endpoint (_hostv4, uint16_t (_port + 1))) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test remoteEndpoint method. - */ -TEST_F (TlsSocket, remoteEndpoint) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.remoteEndpoint (), Tls::Endpoint{}); - ASSERT_EQ (tlsSocket.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.remoteEndpoint (), Tls::Endpoint (_hostv4, _port)) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test opened method. - */ -TEST_F (TlsSocket, opened) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_FALSE (tlsSocket.opened ()); - ASSERT_EQ (tlsSocket.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.opened ()); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.opened ()); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.opened ()); - tlsSocket.close (); - ASSERT_FALSE (tlsSocket.opened ()); -} - -/** - * @brief Test connected method. - */ -TEST_F (TlsSocket, connected) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_FALSE (tlsSocket.connected ()); - ASSERT_EQ (tlsSocket.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.connected ()); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.connected ()); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.connected ()); - tlsSocket.close (); - ASSERT_FALSE (tlsSocket.connected ()); -} - -/** - * @brief Test encrypted method. - */ -TEST_F (TlsSocket, encrypted) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_FALSE (tlsSocket.encrypted ()); - ASSERT_EQ (tlsSocket.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.encrypted ()); - ASSERT_EQ (tlsSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.encrypted ()); - ASSERT_EQ (tlsSocket.startEncryption (), 0) << join::lastError.message (); - ASSERT_TRUE (tlsSocket.encrypted ()); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (tlsSocket.encrypted ()); - tlsSocket.close (); - ASSERT_FALSE (tlsSocket.encrypted ()); -} - -/** - * @brief Test family method. - */ -TEST_F (TlsSocket, family) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.family (), AF_INET); - - ASSERT_EQ (tlsSocket.bind (IpAddress (AF_INET6)), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.family (), AF_INET6); - tlsSocket.close (); - - ASSERT_EQ (tlsSocket.bind (IpAddress (AF_INET)), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.family (), AF_INET); - tlsSocket.close (); -} - -/** - * @brief Test type method. - */ -TEST_F (TlsSocket, type) -{ - Tls::Socket tlsSocket; - - ASSERT_EQ (tlsSocket.type (), SOCK_STREAM); -} - -/** - * @brief Test protocol method. - */ -TEST_F (TlsSocket, protocol) -{ - Tls::Socket tlsSocket; - - ASSERT_EQ (tlsSocket.protocol (), IPPROTO_TCP); -} - -/** - * @brief Test handle method. - */ -TEST_F (TlsSocket, handle) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.handle (), -1); - ASSERT_EQ (tlsSocket.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - ASSERT_GT (tlsSocket.handle (), -1); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_GT (tlsSocket.handle (), -1); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_GT (tlsSocket.handle (), -1); - tlsSocket.close (); - ASSERT_EQ (tlsSocket.handle (), -1); -} - -/** - * @brief Test mtu method. - */ -TEST_F (TlsSocket, mtu) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.mtu (), -1); - ASSERT_EQ (tlsSocket.connectEncrypted ({"127.0.0.1", _port}), 0) << join::lastError.message (); - ASSERT_NE (tlsSocket.mtu (), -1) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_NE (tlsSocket.mtu (), -1) << join::lastError.message (); - tlsSocket.close (); - ASSERT_EQ (tlsSocket.mtu (), -1); - - ASSERT_EQ (tlsSocket.mtu (), -1); - ASSERT_EQ (tlsSocket.connectEncrypted ({"::1", _port}), 0) << join::lastError.message (); - ASSERT_NE (tlsSocket.mtu (), -1) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_NE (tlsSocket.mtu (), -1) << join::lastError.message (); - tlsSocket.close (); - ASSERT_EQ (tlsSocket.mtu (), -1); -} - -/** - * @brief Test checksum method. - */ -TEST_F (TlsSocket, checksum) -{ - std::string buffer ({'\xD2', '\xB6', '\x69', '\xFD', '\x2E'}); - - ASSERT_EQ (Tls::Socket::checksum (reinterpret_cast (&buffer[0]), buffer.size (), 0), 19349); -} - -/** - * @brief Test setCertificate method. - */ -TEST_F (TlsSocket, setCertificate) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.setCertificate ("/invalid/cert/path"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCertificate (_certFile), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCertificate (_certFile, "/invalid/key/path"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCertificate (_certFile, _invalidKey), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCertificate (_certFile, _key), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCertificate (_certFile, _key), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCertificate (_certFile, _key), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test setCaPath method. - */ -TEST_F (TlsSocket, setCaPath) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.setCaPath ("/invalid/ca/path"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCaPath (_certFile), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCaPath (_certPath), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCaPath (_certPath), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCaPath (_certPath), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test setCaFile method. - */ -TEST_F (TlsSocket, setCaFile) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.setCaFile ("/invalid/ca/file"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCaFile (_certPath), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCaFile (_certFile), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCaFile (_certFile), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCaFile (_certFile), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test setVerify method. - */ -TEST_F (TlsSocket, setVerify) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - Tls::Endpoint endpoint{_hostv4, _port}; - - tlsSocket.setVerify (false); - ASSERT_EQ (tlsSocket.connectEncrypted (endpoint), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - - tlsSocket.setVerify (true, 0); - ASSERT_EQ (tlsSocket.connectEncrypted (endpoint), -1); - - tlsSocket.setVerify (true, 1); - ASSERT_EQ (tlsSocket.connectEncrypted (endpoint), -1); - - endpoint.hostname ("localhost"); - tlsSocket.setVerify (true, 0); - ASSERT_EQ (tlsSocket.connectEncrypted (endpoint), -1); - tlsSocket.setVerify (true, 1); - ASSERT_EQ (tlsSocket.connectEncrypted (endpoint), -1); - - ASSERT_EQ (tlsSocket.setCaFile (_rootcert), 0) << join::lastError.message (); - tlsSocket.setVerify (true, 0); - ASSERT_EQ (tlsSocket.connectEncrypted (endpoint), -1); - tlsSocket.setVerify (true, 1); - ASSERT_EQ (tlsSocket.connectEncrypted (endpoint), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - - tlsSocket.close (); -} - -/** - * @brief Test setCipher method. - */ -TEST_F (TlsSocket, setCipher) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.setCipher ("foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCipher (join::defaultCipher), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCipher (join::defaultCipher), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCipher (join::defaultCipher), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test setCipher_1_3 method. - */ -TEST_F (TlsSocket, setCipher_1_3) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.setCipher_1_3 ("foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test setAlpnProtocols method. - */ -TEST_F (TlsSocket, setAlpnProtocols) -{ - Tls::Socket tlsSocket (Tls::Socket::Blocking); - - ASSERT_EQ (tlsSocket.setAlpnProtocols ({""}), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsSocket.setAlpnProtocols ({"http/1.1", "h2"}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.connectEncrypted ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setAlpnProtocols ({"http/1.1", "h2"}), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket.setAlpnProtocols ({"http/1.1", "h2"}), 0) << join::lastError.message (); - tlsSocket.close (); -} - -/** - * @brief Test is lower method. - */ -TEST_F (TlsSocket, isLower) -{ - Tls::Socket tlsSocket1, tlsSocket2; - - ASSERT_EQ (tlsSocket1.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - ASSERT_EQ (tlsSocket2.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - if (tlsSocket1.handle () < tlsSocket2.handle ()) - { - ASSERT_TRUE (tlsSocket1 < tlsSocket2); - } - else - { - ASSERT_TRUE (tlsSocket2 < tlsSocket1); - } - tlsSocket1.close (); - tlsSocket2.close (); -} - -/** - * @brief main function. - */ -int main (int argc, char** argv) -{ - join::initializeOpenSSL (); - testing::InitGoogleTest (&argc, argv); - return RUN_ALL_TESTS (); -} diff --git a/core/tests/tlssocketstream_test.cpp b/core/tests/tlssocketstream_test.cpp deleted file mode 100644 index 1e85dddb..00000000 --- a/core/tests/tlssocketstream_test.cpp +++ /dev/null @@ -1,934 +0,0 @@ -/** - * MIT License - * - * Copyright (c) 2021 Mathieu Rabine - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -// libjoin. -#include -#include - -// Libraries. -#include - -// C++. -#include - -using join::Errc; -using join::IpAddress; -using join::ReactorThread; -using join::EventHandler; -using join::Tls; - -/** - * @brief Class used to test the TLS socket stream API. - */ -class TlsSocketStream : public Tls::Acceptor, public EventHandler, public ::testing::Test -{ -public: - /** - * @brief set up test case. - */ - static void SetUpTestCase () - { - std::ofstream rootCertFile (_rootcert); - if (rootCertFile.is_open ()) - { - rootCertFile << "-----BEGIN CERTIFICATE-----" << std::endl; - rootCertFile << "MIIChjCCAisCFBuHxbqMUGyl7OQUQcoRg3pOBJF+MAoGCCqGSM49BAMCMIHEMQsw" << std::endl; - rootCertFile << "CQYDVQQGEwJGUjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVz" << std::endl; - rootCertFile << "MRcwFQYDVQQKDA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdv" << std::endl; - rootCertFile << "cmsgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3" << std::endl; - rootCertFile << "b3JrLm5ldDEoMCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5l" << std::endl; - rootCertFile << "dDAeFw0yMjA3MDUxNjMxMTZaFw0zMjA3MDIxNjMxMTZaMIHEMQswCQYDVQQGEwJG" << std::endl; - rootCertFile << "UjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVzMRcwFQYDVQQK" << std::endl; - rootCertFile << "DA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdvcmsgQ2VydGlm" << std::endl; - rootCertFile << "aWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3b3JrLm5ldDEo" << std::endl; - rootCertFile << "MCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5ldDBZMBMGByqG" << std::endl; - rootCertFile << "SM49AgEGCCqGSM49AwEHA0IABASk0zCrKtXQi0Ycx+Anx+VWv8gncbPmNQ1yutii" << std::endl; - rootCertFile << "gQjP2mF9NIqlxpcKNuE/6DDnfSzCEDhFyvGiK0NJ1C3RBowwCgYIKoZIzj0EAwID" << std::endl; - rootCertFile << "SQAwRgIhAIFqdbxTb5kRjy4UY0N205ZEhHSMK89p2oUyn4iNbXH2AiEAtmV1UyRX" << std::endl; - rootCertFile << "DIAGr/F+1SwQMPoJzSQxZ7NdxjNgW286e9Q=" << std::endl; - rootCertFile << "-----END CERTIFICATE-----" << std::endl; - rootCertFile.close (); - } - - mkdir (_certPath.c_str (), 0777); - std::ofstream certFile (_certFile); - if (certFile.is_open ()) - { - certFile << "-----BEGIN CERTIFICATE-----" << std::endl; - certFile << "MIIDgDCCAyagAwIBAgIUR3ZIuKMt0BdaOZQnPwhSMR9qzfgwCgYIKoZIzj0EAwIw" << std::endl; - certFile << "gcQxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nh" << std::endl; - certFile << "c3RyZXMxFzAVBgNVBAoMDkpvaW4gRnJhbWV3b3JrMS0wKwYDVQQLDCRKb2luIEZy" << std::endl; - certFile << "YW1ld29yayBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHTAbBgNVBAMMFGNhLmpvaW5m" << std::endl; - certFile << "cmFtZXdvcmsubmV0MSgwJgYJKoZIhvcNAQkBFhlzdXBwb3J0QGpvaW5mcmFtZXdv" << std::endl; - certFile << "cmsubmV0MB4XDTIyMDcwNzEyMTIxMFoXDTMyMDcwNDEyMTIxMFowgagxCzAJBgNV" << std::endl; - certFile << "BAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nhc3RyZXMxFzAV" << std::endl; - certFile << "BgNVBAoMDkpvaW4gRnJhbWV3b3JrMRswGQYDVQQLDBJKb2luIEZyYW1ld29yayBE" << std::endl; - certFile << "ZXYxEzARBgNVBAMMCmxvY2FsaG9zdC4xKDAmBgkqhkiG9w0BCQEWGXN1cHBvcnRA" << std::endl; - certFile << "am9pbmZyYW1ld29yay5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" << std::endl; - certFile << "AQDSNtw5zEoJFPf6Rl0Y1n8BQfE0YTPCELvFAeioUfj8CAnUleHL9pwAEFg6kgoG" << std::endl; - certFile << "hvwto5/yWGPUqNNfe3xbFTJcHgMhgtjqy5H6sYDkTi3kYIIMBfTHr8NI7HWE8Nz1" << std::endl; - certFile << "qU1snjtERnkoLilIZf/2BojNVMtHC1H316WbMicXS0v7HQo3lv6PYSana9Q9ow9O" << std::endl; - certFile << "2/FiW5qq1eOhI1ZedRanX+bl0jHWCd3WsI87+5bTaQrfetdHTOmav6O17Iq9FiTh" << std::endl; - certFile << "Sg9fbM3s2Hw15kI+mws029dhcwXs5sYY+NgtrQwjR5qH+54BdUaPwQfl/KyulfEl" << std::endl; - certFile << "TJykJ+3w6MorxUr55F68uBNbAgMBAAGjRTBDMAsGA1UdDwQEAwIF4DAdBgNVHSUE" << std::endl; - certFile << "FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwFQYDVR0RBA4wDIIKbG9jYWxob3N0LjAK" << std::endl; - certFile << "BggqhkjOPQQDAgNIADBFAiA120ufIbhcw7BJQ1L6WudDdW2mHrVXvdgeOzVGgz1d" << std::endl; - certFile << "iAIhAMm/sWI3yzb2IMPffxWKYusWEQE2hZvs24ESSC/ZZ0s+" << std::endl; - certFile << "-----END CERTIFICATE-----" << std::endl; - certFile.close (); - } - - [[maybe_unused]] int result; - result = std::system (std::string ("/usr/bin/c_rehash " + _certPath).c_str ()); - - std::ofstream keyFile (_key); - if (keyFile.is_open ()) - { - keyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; - keyFile << "MIIEowIBAAKCAQEA0jbcOcxKCRT3+kZdGNZ/AUHxNGEzwhC7xQHoqFH4/AgJ1JXh" << std::endl; - keyFile << "y/acABBYOpIKBob8LaOf8lhj1KjTX3t8WxUyXB4DIYLY6suR+rGA5E4t5GCCDAX0" << std::endl; - keyFile << "x6/DSOx1hPDc9alNbJ47REZ5KC4pSGX/9gaIzVTLRwtR99elmzInF0tL+x0KN5b+" << std::endl; - keyFile << "j2Emp2vUPaMPTtvxYluaqtXjoSNWXnUWp1/m5dIx1gnd1rCPO/uW02kK33rXR0zp" << std::endl; - keyFile << "mr+jteyKvRYk4UoPX2zN7Nh8NeZCPpsLNNvXYXMF7ObGGPjYLa0MI0eah/ueAXVG" << std::endl; - keyFile << "j8EH5fysrpXxJUycpCft8OjKK8VK+eRevLgTWwIDAQABAoIBAAzdlK7o5OMXaHHl" << std::endl; - keyFile << "2o7Jme5Oxd9pz4wiEAvnqQCcO7vZFhjvr2kXR8btOSkkhP6PRmHYsNJZPIroZj9i" << std::endl; - keyFile << "xGKisnlW0OQ9KN995ApO0M+oRUDD81GfD7Mk+7O73Rls0GksmnN6X7A3C/U8lgQ7" << std::endl; - keyFile << "UeYR0k+Wz/YiKDsd9KHB+QiA8D6HFQ9I8Y2P97KOcYnxXZfSwNm+ENNU3wShZOl2" << std::endl; - keyFile << "ZYJJ4DE+5m2SwZ6g8b5Zre4cDbOduwuz/jXzjy2tAZBlTS4DVpYlhd14z+ssUWiu" << std::endl; - keyFile << "AdS/nqSF7Obj0TRhoGNfrkisFzV4itavQ5DKGj/6hjueIJVLteUOzcCeg26YosNy" << std::endl; - keyFile << "QzZSjOECgYEA7y3InEoh93/4HZCZmdwN8KfZtqirX0t966FntgAT8RkIs+KvNS8B" << std::endl; - keyFile << "m3RfNLa/EuDt5zTmHRGx+oeN+17i9QQjKWcR0NnJ6aSZbvJByj3yKxLF9XVllzp/" << std::endl; - keyFile << "vHSSyB264RoKIrWmFN6cCO4u4h9ZPY75pASWBCDMdnGK8axAcqAnlqsCgYEA4P+Y" << std::endl; - keyFile << "FF9RW4rhrVU4dpXSfcr6vOwqfp9F9vhTVL0JS/SLOFoJNNpS9Rnq3pVLEuKyCphd" << std::endl; - keyFile << "3nk9VFfoRygmMaGBvwGaXZPPvosoaIUgOdTt7KIfSHPichBEVxRuWCrtTGGkG0ok" << std::endl; - keyFile << "s/RPHhvxZE267vsVj1PktK8Yr5Ba0AL2ycztNhECgYB5OAwHYe8LIBlg6otelk+e" << std::endl; - keyFile << "W4OU9rE8L+eWx4vniuyQce6eNNI1syguYHFsJv56E/OfDYlezDwWzCLidnmyUjF7" << std::endl; - keyFile << "51f5MJgLyTdWKoO7e1/EAtS/jYs6dRSOL8rAj4jKU0c1xjhxNU2BnS23vsmc0Fyn" << std::endl; - keyFile << "iwd4+iKGGQ+hYnqbXZ4S1wKBgD/3an0gPDkSWua0e8D7B0TMGEztt4cYMQPtxYMp" << std::endl; - keyFile << "2yLE+2+h6UwlZcBZBfUR7K4J1SQ9/THqtgzskRTpzTH/AKwVAJXqF/3MAkj00Byg" << std::endl; - keyFile << "9KN50/r9NzvGdCdtn5FhYuV8PPOlOJoQsw2UVCR4FNUsfQyqhTL5NMN0/tx0e0UU" << std::endl; - keyFile << "BbyBAoGBANu5ifByauVELH8UEl5rXRu1S9iAVV+Bc5jboXwc4VxJtEyomGJ7+YdL" << std::endl; - keyFile << "5c9LFV+STUp7CE12uSXQZTQM0tEjPinLntRinNzu9tIHR1vy7FZHEwMFIgB4VTY7" << std::endl; - keyFile << "ALRYv1/QpTuywpNUFRS15JkfGNf5JIkrUEWLgkX3OVCBsRGHUugy" << std::endl; - keyFile << "-----END RSA PRIVATE KEY-----" << std::endl; - keyFile.close (); - } - - std::ofstream invalidKeyFile (_invalidKey); - if (invalidKeyFile.is_open ()) - { - invalidKeyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; - invalidKeyFile << "MIIEowIBAAKCAQEA2Q0DOyG039uVMuxNnZ5fpfOcvXXOTguST1QR6eLVkdG7OKpM" << std::endl; - invalidKeyFile << "nc9K597jx1syT1q+SwFcykMtvWxCfD8BR7bcLILeO6z+HlRfvjOhUiHaX/KCaTN8" << std::endl; - invalidKeyFile << "l7OJOgmUlL0FhQ1SXxw7KCSGd+rgu1iHwjFDDkj/tG24ashdmNt+DYdeoJu2mzgw" << std::endl; - invalidKeyFile << "tEASfG9VjqBR7ni4Hg/sRpwXvEK5nI1JSLyZbcPCxGlBRdB8hMdny/VW+SBwKD2/" << std::endl; - invalidKeyFile << "ivpVJLulw2oniSIcCCcr9d+ERY4XrO71UsiACwPxfdEtbG0KrZfpK91k7vl64DHM" << std::endl; - invalidKeyFile << "CeTQPKRZm+LDKOUfv/eTF9F6GY4Dpw2LMwLM5QIDAQABAoIBABjV91etzK+Mxa61" << std::endl; - invalidKeyFile << "AVCWzaUEkhvPvhKKGmy/VulnTj7IO98JBYlNLeoIRBIMql4QKRQWDNMMCtDQ8W6c" << std::endl; - invalidKeyFile << "Gv5kux7QvrMfYViBGQ9/gucN/pnZ+vgkrw4AuiQM8pZuZpJJ6vH9HfvC6iwQkTR+" << std::endl; - invalidKeyFile << "tdIPpvecfL3djCuTz7ns66iKo9ZGpRE6emTBynr8og/oqD8Vw5bW+JJ+AJ3IqZf4" << std::endl; - invalidKeyFile << "NslNist7d5FZ5N/+nxWyBUcFglP7bZzb/raOVc/flrYIeDy72asnWOYbDTPzMyH1" << std::endl; - invalidKeyFile << "dfaox6QKZtA5NdO9x4aHHGgAz8BTgqs7LvxPwoH+XF1dDCsb3kIeQxHTfcc1opMw" << std::endl; - invalidKeyFile << "atxpgwECgYEA8Zq/7Z3tKcBlMz4XNKWWvaDxhBUIS62tGeLJ2spLRFvkL1ixnjcK" << std::endl; - invalidKeyFile << "72YWOwDpoINEWa8AhAhM6afE9VxrupSGg+C9uALaJ8HTWTP6u6/F8sbsYaoWHyA/" << std::endl; - invalidKeyFile << "k/8/nFEr43ciKUjBhMHB42vYidAgiOvDVXc+/k7HIMQfl/vyp32ecEECgYEA5fu9" << std::endl; - invalidKeyFile << "ePLh55TYbXe8SCL0hsZcC8Q/ioT/0GJ6uevGb0lw3XAa+HC6//upu90T7ZOIqysc" << std::endl; - invalidKeyFile << "aAqln7ZEeCfvXI/3YJyJ2RWatD+2itECbd0WV2/JflO/OAzDSSFvpxxmwIzccIeA" << std::endl; - invalidKeyFile << "UNuNcQGD8HDwFzU+sULvF82yuwMt1syPd/mns6UCgYAviqP5vfnNHW7MhotKcMsY" << std::endl; - invalidKeyFile << "xXLA6uKXAbXuQhI2W1g0O2DLcEiDOZGNSilVsvhF/Y6VlzoiwP9hewHmxijsrg1K" << std::endl; - invalidKeyFile << "Jg8vBmCnMhzEkNXl2NC61SnujemMdmwMU03RFKfuOqMePJLX7MiaV75kX/AHAV2O" << std::endl; - invalidKeyFile << "k8hxgk7sw6rz3UACdVWYAQKBgHUu5ScoksS+Cd0VQmF7Nh8qGSKBt2KsS/BxDVmI" << std::endl; - invalidKeyFile << "ck6oHBMomQV340CliaHIjuvh3aRhzhKRQjzz0UVsC8GdNY4LlQ2AvZgUUr2+q78x" << std::endl; - invalidKeyFile << "BL4+nmt43pj/n822dL6wcQaxf2zzDgWlKReojwLHeP5KSgxmL49wZx51CzlEd+HI" << std::endl; - invalidKeyFile << "2pNlAoGBAObdC7woN7jEfdfYz1BhUpmBsIRqW2yLA1DnlK9lfgs2i1w7spzAh2hV" << std::endl; - invalidKeyFile << "djPiKj5vZdcrbaa+SBAnZbFTHyXmAbKbO/iZpSromaZYyCK8NktJu/YxpWZmjnRF" << std::endl; - invalidKeyFile << "2xOadRGCav5fTGzCN/ADLgIo4gIAI2o/UnV/MdaSAdHyIeSrxBAb" << std::endl; - invalidKeyFile << "-----END RSA PRIVATE KEY-----" << std::endl; - invalidKeyFile.close (); - } - } - - /** - * @brief tear down test case. - */ - static void TearDownTestCase () - { - unlink (_rootcert.c_str ()); - unlink (_certFile.c_str ()); - rmdir (_certPath.c_str ()); - unlink (_key.c_str ()); - unlink (_invalidKey.c_str ()); - } - -protected: - /** - * @brief Sets up the test fixture. - */ - void SetUp () override - { - ASSERT_EQ (this->setCertificate (_certFile, _key), 0) << join::lastError.message (); - ASSERT_EQ (this->setCipher (join::defaultCipher), 0) << join::lastError.message (); - ASSERT_EQ (this->setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); - ASSERT_EQ (this->create ({IpAddress::ipv6Wildcard, _port}), 0) << join::lastError.message (); - ASSERT_EQ (ReactorThread::reactor ().addHandler (handle (), this), 0) << join::lastError.message (); - } - - /** - * @brief Tears down the test fixture. - */ - void TearDown () override - { - ASSERT_EQ (ReactorThread::reactor ().delHandler (handle ()), 0) << join::lastError.message (); - this->close (); - } - - /** - * @brief method called when data are ready to be read on handle. - * @param fd file descriptor. - */ - virtual void onReadable ([[maybe_unused]] int fd) override - { - Tls::Stream stream = this->acceptStreamEncrypted (); - if (stream.connected ()) - { - char buf[1024]; - for (;;) - { - // echo received data. - int nread = stream.socket ().read (buf, sizeof (buf)); - if (nread == -1) - { - if (join::lastError == Errc::TemporaryError) - { - if (stream.socket ().waitReadyRead (_timeout)) - continue; - } - break; - } - stream.socket ().writeExactly (buf, nread); - } - stream.close (); - } - } - - /// host. - static const std::string _host; - - /// port. - static const uint16_t _port; - static const uint16_t _invalid_port; - - /// timeout. - static const int _timeout; - - /// root certificate. - static const std::string _rootcert; - - /// certificate path. - static const std::string _certPath; - - /// certificate file. - static const std::string _certFile; - - /// private key. - static const std::string _key; - - /// invalid private key. - static const std::string _invalidKey; -}; - -const std::string TlsSocketStream::_host = "127.0.0.1"; -const uint16_t TlsSocketStream::_port = 5000; -const uint16_t TlsSocketStream::_invalid_port = 5032; -const int TlsSocketStream::_timeout = 1000; -const std::string TlsSocketStream::_rootcert = "/tmp/tlssocket_test_root.cert"; -const std::string TlsSocketStream::_certPath = "/tmp/certs"; -const std::string TlsSocketStream::_certFile = _certPath + "/tlssocket_test.cert"; -const std::string TlsSocketStream::_key = "/tmp/tlssocket_test.key"; -const std::string TlsSocketStream::_invalidKey = "/tmp/tlssocket_test_invalid.key"; - -/** - * @brief Test default constructor. - */ -TEST_F (TlsSocketStream, defaultConstruct) -{ - Tls::Stream tlsStream; - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); -} - -/** - * @brief Test move constructor. - */ -TEST_F (TlsSocketStream, moveConstruct) -{ - Tls::Stream tmp; - ASSERT_TRUE (tmp.good ()) << join::lastError.message (); - Tls::Stream tlsStream (std::move (tmp)); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); -} - -/** - * @brief Test move operatore. - */ -TEST_F (TlsSocketStream, moveAssign) -{ - Tls::Stream tmp; - ASSERT_TRUE (tmp.good ()) << join::lastError.message (); - Tls::Stream tlsStream; - tlsStream = std::move (tmp); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); -} - -/** - * @brief Test bind method. - */ -TEST_F (TlsSocketStream, bind) -{ - Tls::Stream tlsStream; - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.bind (_host); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.bind (_host); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test connect method. - */ -TEST_F (TlsSocketStream, connect) -{ - Tls::Stream tlsStream; - tlsStream.connect ({"255.255.255.255", _port}); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - tlsStream.connect ({_host, _invalid_port}); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.fail ()); - ASSERT_EQ (join::lastError, Errc::InUse); - tlsStream.clear (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test startEncryption method. - */ -TEST_F (TlsSocketStream, startEncryption) -{ - Tls::Stream tlsStream; - tlsStream.startEncryption (); - ASSERT_TRUE (tlsStream.fail ()); - ASSERT_EQ (join::lastError, Errc::OperationFailed); - tlsStream.clear (); - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.startEncryption (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.startEncryption (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test connectEncrypted method. - */ -TEST_F (TlsSocketStream, connectEncrypted) -{ - Tls::Stream tlsStream; - tlsStream.connectEncrypted ({"255.255.255.255", _port}); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - tlsStream.connectEncrypted ({_host, _invalid_port}); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test disconnect method. - */ -TEST_F (TlsSocketStream, disconnect) -{ - Tls::Stream tlsStream; - ASSERT_FALSE (tlsStream.connected ()); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_TRUE (tlsStream.connected ()); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_FALSE (tlsStream.connected ()); - tlsStream.close (); - ASSERT_FALSE (tlsStream.connected ()); -} - -/** - * @brief Test close method. - */ -TEST_F (TlsSocketStream, close) -{ - Tls::Stream tlsStream; - ASSERT_FALSE (tlsStream.opened ()); - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_TRUE (tlsStream.opened ()); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_FALSE (tlsStream.opened ()); - tlsStream.close (); - ASSERT_FALSE (tlsStream.opened ()); -} - -/** - * @brief Test localEndpoint method. - */ -TEST_F (TlsSocketStream, localEndpoint) -{ - Tls::Stream tlsStream; - ASSERT_EQ (tlsStream.localEndpoint (), Tls::Endpoint{}); - tlsStream.bind ({_host, uint16_t (_port + 1)}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.localEndpoint (), Tls::Endpoint (_host, uint16_t (_port + 1))) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test remoteEndpoint method. - */ -TEST_F (TlsSocketStream, remoteEndpoint) -{ - Tls::Stream tlsStream; - ASSERT_EQ (tlsStream.remoteEndpoint (), Tls::Endpoint{}); - tlsStream.bind ({_host, uint16_t (_port + 1)}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.remoteEndpoint (), Tls::Endpoint (_host, _port)) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test opened method. - */ -TEST_F (TlsSocketStream, opened) -{ - Tls::Stream tlsStream; - ASSERT_FALSE (tlsStream.opened ()); - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_TRUE (tlsStream.opened ()); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_FALSE (tlsStream.opened ()); - tlsStream.close (); - ASSERT_FALSE (tlsStream.opened ()); -} - -/** - * @brief Test connected method. - */ -TEST_F (TlsSocketStream, connected) -{ - Tls::Stream tlsStream; - ASSERT_FALSE (tlsStream.connected ()); - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_TRUE (tlsStream.connected ()); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_FALSE (tlsStream.connected ()); - tlsStream.close (); - ASSERT_FALSE (tlsStream.connected ()); -} - -/** - * @brief Test encrypted method. - */ -TEST_F (TlsSocketStream, encrypted) -{ - Tls::Stream tlsStream; - ASSERT_FALSE (tlsStream.encrypted ()); - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_FALSE (tlsStream.encrypted ()); - tlsStream.startEncryption (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_TRUE (tlsStream.encrypted ()); - tlsStream.disconnect (); - ASSERT_FALSE (tlsStream.encrypted ()); - tlsStream.close (); - ASSERT_FALSE (tlsStream.encrypted ()); -} - -/** - * @brief Test setCertificate method. - */ -TEST_F (TlsSocketStream, setCertificate) -{ - Tls::Stream tlsStream; - ASSERT_EQ (tlsStream.setCertificate ("/invalid/cert/path"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCertificate (_certFile), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCertificate (_certFile, "/invalid/key/path"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCertificate (_certFile, _invalidKey), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCertificate (_certFile, _key), 0) << join::lastError.message (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCertificate (_certFile, _key), 0) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCertificate (_certFile, _key), 0) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test setCaPath method. - */ -TEST_F (TlsSocketStream, setCaPath) -{ - Tls::Stream tlsStream; - ASSERT_EQ (tlsStream.setCaPath ("/invalid/ca/path"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCaPath (_certFile), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCaPath (_certPath), 0) << join::lastError.message (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCaPath (_certPath), 0) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCaPath (_certPath), 0) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test setCaFile method. - */ -TEST_F (TlsSocketStream, setCaFile) -{ - Tls::Stream tlsStream; - ASSERT_EQ (tlsStream.setCaFile ("/invalid/ca/file"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCaFile (_certPath), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCaFile (_certFile), 0) << join::lastError.message (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCaFile (_certFile), 0) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCaFile (_certFile), 0) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test setVerify method. - */ -TEST_F (TlsSocketStream, setVerify) -{ - Tls::Stream tlsStream; - Tls::Endpoint endpoint{_host, _port}; - - tlsStream.setVerify (false); - tlsStream.connectEncrypted (endpoint); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - - tlsStream.setVerify (true, 0); - tlsStream.connectEncrypted (endpoint); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - - tlsStream.setVerify (true, 1); - tlsStream.connectEncrypted (endpoint); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - - endpoint.hostname ("localhost"); - tlsStream.setVerify (true, 0); - tlsStream.connectEncrypted (endpoint); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - tlsStream.setVerify (true, 1); - tlsStream.connectEncrypted (endpoint); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - - ASSERT_EQ (tlsStream.setCaFile (_rootcert), 0) << join::lastError.message (); - tlsStream.setVerify (true, 0); - tlsStream.connectEncrypted (endpoint); - ASSERT_TRUE (tlsStream.fail ()); - tlsStream.clear (); - tlsStream.setVerify (true, 1); - tlsStream.connectEncrypted (endpoint); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.clear (); - - tlsStream.close (); -} - -/** - * @brief Test setCipher method. - */ -TEST_F (TlsSocketStream, setCipher) -{ - Tls::Stream tlsStream; - ASSERT_EQ (tlsStream.setCipher ("foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCipher (join::defaultCipher), 0) << join::lastError.message (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCipher (join::defaultCipher), 0) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCipher (join::defaultCipher), 0) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test setCipher_1_3 method. - */ -TEST_F (TlsSocketStream, setCipher_1_3) -{ - Tls::Stream tlsStream; - ASSERT_EQ (tlsStream.setCipher_1_3 ("foo"), -1); - ASSERT_EQ (join::lastError, Errc::InvalidParam); - ASSERT_EQ (tlsStream.setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.setCipher_1_3 (join::defaultCipher_1_3), 0) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test timeout method. - */ -TEST_F (TlsSocketStream, timeout) -{ - Tls::Stream tlsStream; - ASSERT_NE (tlsStream.timeout (), _timeout); - tlsStream.timeout (_timeout); - ASSERT_EQ (tlsStream.timeout (), _timeout); -} - -/** - * @brief Test socket method. - */ -TEST_F (TlsSocketStream, socket) -{ - Tls::Stream tlsStream; - ASSERT_EQ (tlsStream.socket ().handle (), -1); - tlsStream.connect ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_NE (tlsStream.socket ().handle (), -1); - tlsStream.close (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - ASSERT_EQ (tlsStream.socket ().handle (), -1); -} - -/** - * @brief Test insert operator. - */ -TEST_F (TlsSocketStream, insert) -{ - Tls::Stream tlsStream; - tlsStream << "test" << std::endl; - ASSERT_TRUE (tlsStream.fail ()); - ASSERT_EQ (join::lastError, Errc::ConnectionClosed); - tlsStream.clear (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream << "test" << std::endl; - tlsStream.flush (); - ASSERT_TRUE (tlsStream.socket ().waitReadyRead (_timeout)); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test put method. - */ -TEST_F (TlsSocketStream, put) -{ - Tls::Stream tlsStream; - tlsStream.put ('t'); - ASSERT_TRUE (tlsStream.fail ()); - ASSERT_EQ (join::lastError, Errc::ConnectionClosed); - tlsStream.clear (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.put ('t'); - tlsStream.put ('e'); - tlsStream.put ('s'); - tlsStream.put ('t'); - tlsStream.flush (); - ASSERT_TRUE (tlsStream.socket ().waitReadyRead (_timeout)); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test write method. - */ -TEST_F (TlsSocketStream, write) -{ - Tls::Stream tlsStream; - tlsStream.write ("test", 4); - ASSERT_TRUE (tlsStream.fail ()); - ASSERT_EQ (join::lastError, Errc::ConnectionClosed); - tlsStream.clear (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.write ("test", 4); - tlsStream.flush (); - ASSERT_TRUE (tlsStream.socket ().waitReadyRead (_timeout)); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test flush method. - */ -TEST_F (TlsSocketStream, flush) -{ - Tls::Stream tlsStream; - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.put ('t'); - tlsStream.flush (); - tlsStream.put ('e'); - tlsStream.flush (); - tlsStream.put ('s'); - tlsStream.flush (); - tlsStream.put ('t'); - tlsStream.flush (); - ASSERT_TRUE (tlsStream.socket ().waitReadyRead (_timeout)); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test extract method. - */ -TEST_F (TlsSocketStream, extract) -{ - int test; - Tls::Stream tlsStream; - tlsStream >> test; - ASSERT_TRUE (tlsStream.fail ()); - ASSERT_EQ (join::lastError, Errc::ConnectionClosed); - tlsStream.clear (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream << int (123456789) << std::endl; - tlsStream.flush (); - tlsStream >> test; - ASSERT_EQ (test, 123456789); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test get method. - */ -TEST_F (TlsSocketStream, get) -{ - Tls::Stream tlsStream; - tlsStream.get (); - ASSERT_TRUE (tlsStream.fail ()); - ASSERT_EQ (join::lastError, Errc::ConnectionClosed); - tlsStream.clear (); - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.write ("test", 4); - tlsStream.flush (); - ASSERT_EQ (tlsStream.get (), 't'); - ASSERT_EQ (tlsStream.get (), 'e'); - ASSERT_EQ (tlsStream.get (), 's'); - ASSERT_EQ (tlsStream.get (), 't'); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test peek method. - */ -TEST_F (TlsSocketStream, peek) -{ - Tls::Stream tlsStream; - tlsStream.connectEncrypted ({_host, _port}); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.write ("test", 4); - tlsStream.flush (); - ASSERT_EQ (tlsStream.peek (), 't'); - ASSERT_EQ (tlsStream.get (), 't'); - ASSERT_EQ (tlsStream.peek (), 'e'); - ASSERT_EQ (tlsStream.get (), 'e'); - ASSERT_EQ (tlsStream.peek (), 's'); - ASSERT_EQ (tlsStream.get (), 's'); - ASSERT_EQ (tlsStream.peek (), 't'); - ASSERT_EQ (tlsStream.get (), 't'); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test getline method. - */ -TEST_F (TlsSocketStream, getline) -{ - Tls::Stream tlsStream; - tlsStream.connectEncrypted ({_host, _port}); - tlsStream.write ("test\n", 5); - tlsStream.flush (); - std::array test = {}; - tlsStream.getline (test.data (), test.size (), '\n'); - ASSERT_STREQ (test.data (), "test"); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test ignore method. - */ -TEST_F (TlsSocketStream, ignore) -{ - Tls::Stream tlsStream; - tlsStream.connectEncrypted ({_host, _port}); - tlsStream.write ("test\n", 5); - tlsStream.flush (); - tlsStream.ignore (std::numeric_limits::max (), 'e'); - ASSERT_EQ (tlsStream.get (), 's'); - ASSERT_EQ (tlsStream.get (), 't'); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test read method. - */ -TEST_F (TlsSocketStream, read) -{ - Tls::Stream tlsStream; - tlsStream.connectEncrypted ({_host, _port}); - tlsStream.write ("test", 4); - tlsStream.flush (); - std::array test = {}; - tlsStream.read (test.data (), 4); - ASSERT_STREQ (test.data (), "test"); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test readsome method. - */ -TEST_F (TlsSocketStream, DISABLED_readsome) -{ - Tls::Stream tlsStream; - tlsStream.connectEncrypted ({_host, _port}); - tlsStream.write ("test", 4); - tlsStream.flush (); - std::array test = {}; - ASSERT_EQ (tlsStream.readsome (test.data (), test.size ()), 4); - ASSERT_STREQ (test.data (), "test"); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief Test gcount method. - */ -TEST_F (TlsSocketStream, gcount) -{ - Tls::Stream tlsStream; - tlsStream.connectEncrypted ({_host, _port}); - tlsStream.write ("test", 4); - tlsStream.flush (); - std::array test = {}; - tlsStream.read (test.data (), 4); - ASSERT_EQ (tlsStream.gcount (), 4); - tlsStream.disconnect (); - ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); - tlsStream.close (); -} - -/** - * @brief main function. - */ -int main (int argc, char** argv) -{ - join::initializeOpenSSL (); - testing::InitGoogleTest (&argc, argv); - return RUN_ALL_TESTS (); -} diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 7d901561..feb1afb8 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -1,23 +1,27 @@ cmake_minimum_required(VERSION 3.22.1) +find_package(OpenSSL REQUIRED) + set(PUBLIC_HEADERS include/join/base64.hpp - include/join/digest.hpp + include/join/openssl.hpp + include/join/openssl.hpp include/join/hmac.hpp include/join/tlskey.hpp include/join/signature.hpp - # include/join/tlserror.hpp + include/join/tlserror.hpp include/join/tlscontext.hpp include/join/tlswrapper.hpp ) set(SOURCES src/base64.cpp + src/openssl.cpp src/digest.cpp src/hmac.cpp src/tlskey.cpp src/signature.cpp - # src/tlserror.cpp + src/tlserror.cpp src/tlscontext.cpp ) @@ -41,6 +45,8 @@ target_include_directories(${JOIN_CRYPTO} target_link_libraries(${JOIN_CRYPTO} PUBLIC ${JOIN_CORE} + OpenSSL::SSL + OpenSSL::Crypto ) install(TARGETS ${JOIN_CRYPTO} diff --git a/core/include/join/openssl.hpp b/crypto/include/join/openssl.hpp similarity index 99% rename from core/include/join/openssl.hpp rename to crypto/include/join/openssl.hpp index baf4628e..d3f9d3ff 100644 --- a/core/include/join/openssl.hpp +++ b/crypto/include/join/openssl.hpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#ifndef JOIN_CORE_OPENSSL_HPP -#define JOIN_CORE_OPENSSL_HPP +#ifndef JOIN_CRYPTO_OPENSSL_HPP +#define JOIN_CRYPTO_OPENSSL_HPP // Libraries. #include diff --git a/crypto/include/join/tlswrapper.hpp b/crypto/include/join/tlswrapper.hpp index e8b246e3..85a66b2d 100644 --- a/crypto/include/join/tlswrapper.hpp +++ b/crypto/include/join/tlswrapper.hpp @@ -27,6 +27,7 @@ // libjoin. #include +#include #include #include @@ -42,6 +43,7 @@ namespace join class TlsWrapper { public: + using UnderlyingSocket = Socket; using Protocol = typename Socket::Proto; using Mode = typename Socket::Mode; using Option = typename Socket::Option; @@ -52,9 +54,8 @@ namespace join * @brief create a TLS wrapper with an internally created socket. * @param ctx TLS context. */ - explicit TlsWrapper (TlsContext ctx, Mode mode = Socket::Mode::NonBlocking) noexcept - : _socket (mode) - , _ctx (ctx) + explicit TlsWrapper (TlsContext ctx, Mode mode = Mode::NonBlocking) noexcept + : TlsWrapper (Socket{mode}, ctx) { } @@ -803,7 +804,7 @@ namespace join * @param val option value. * @return 0 on success, -1 on failure. */ - int setOption (typename Socket::Option opt, int val) noexcept + int setOption (Option opt, int val) noexcept { return _socket.setOption (opt, val); } diff --git a/core/src/openssl.cpp b/crypto/src/openssl.cpp similarity index 100% rename from core/src/openssl.cpp rename to crypto/src/openssl.cpp diff --git a/crypto/tests/CMakeLists.txt b/crypto/tests/CMakeLists.txt index 28de04a5..61feb6b2 100644 --- a/crypto/tests/CMakeLists.txt +++ b/crypto/tests/CMakeLists.txt @@ -7,6 +7,11 @@ target_link_libraries(base64.gtest ${JOIN_CRYPTO} GTest::gtest_main) add_test(NAME base64.gtest COMMAND base64.gtest) install(TARGETS base64.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(openssl.gtest openssl_test.cpp) +target_link_libraries(openssl.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME openssl.gtest COMMAND openssl.gtest) +install(TARGETS openssl.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + add_executable(digesterror.gtest digesterror_test.cpp) target_link_libraries(digesterror.gtest ${JOIN_CRYPTO} GTest::gtest_main) add_test(NAME digesterror.gtest COMMAND digesterror.gtest) diff --git a/core/tests/openssl_test.cpp b/crypto/tests/openssl_test.cpp similarity index 100% rename from core/tests/openssl_test.cpp rename to crypto/tests/openssl_test.cpp diff --git a/fabric/CMakeLists.txt b/fabric/CMakeLists.txt index 4de62258..b566a8b1 100644 --- a/fabric/CMakeLists.txt +++ b/fabric/CMakeLists.txt @@ -1,9 +1,14 @@ cmake_minimum_required(VERSION 3.22.1) set(PUBLIC_HEADERS + include/join/dns.hpp + include/join/dot.hpp + include/join/mdns.hpp include/join/dnsmessage.hpp include/join/resolver.hpp include/join/nameserver.hpp + include/join/netlink.hpp + include/join/netlink_endpoint.hpp include/join/netlinkmanager.hpp include/join/neighbor.hpp include/join/neighbormanager.hpp @@ -44,6 +49,7 @@ target_include_directories(${JOIN_FABRIC} target_link_libraries(${JOIN_FABRIC} PUBLIC + ${JOIN_CRYPTO} ${JOIN_CORE} ) diff --git a/fabric/include/join/dns.hpp b/fabric/include/join/dns.hpp new file mode 100644 index 00000000..02ede0c5 --- /dev/null +++ b/fabric/include/join/dns.hpp @@ -0,0 +1,140 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_FABRIC_DNS_HPP +#define JOIN_FABRIC_DNS_HPP + +// libjoin. +#include + +namespace join +{ + template + class BasicDatagramResolver; + + template + class BasicDatagramNameServer; + + /** + * @brief DNS over UDP protocol class. + */ + class Dns + { + public: + using Endpoint = BasicInternetEndpoint; + using Socket = BasicDatagramSocket; + using Resolver = BasicDatagramResolver; + using NameServer = BasicDatagramNameServer; + + /** + * @brief construct the DNS protocol instance. + * @param family IP address family. + */ + constexpr Dns (int family = AF_INET) noexcept + : _family (family) + { + } + + /** + * @brief get protocol suitable for IPv4 address family. + * @return an IPv4 address family suitable protocol. + */ + static inline Dns& v4 () noexcept + { + static Dns dnsv4 (AF_INET); + return dnsv4; + } + + /** + * @brief get protocol suitable for IPv6 address family. + * @return an IPv6 address family suitable protocol. + */ + static inline Dns& v6 () noexcept + { + static Dns dnsv6 (AF_INET6); + return dnsv6; + } + + /** + * @brief get the protocol IP address family. + * @return the protocol IP address family. + */ + constexpr int family () const noexcept + { + return _family; + } + + /** + * @brief get the protocol communication semantic. + * @return the protocol communication semantic. + */ + constexpr int type () const noexcept + { + return SOCK_DGRAM; + } + + /** + * @brief get the protocol type. + * @return the protocol type. + */ + constexpr int protocol () const noexcept + { + return IPPROTO_UDP; + } + + /// default DNS port. + static constexpr uint16_t defaultPort = 53; + + /// maximum DNS message size. + static constexpr size_t maxMsgSize = 8192; + + private: + /// IP address family. + int _family; + }; + + /** + * @brief check if equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if equals. + */ + constexpr bool operator== (const Dns& a, const Dns& b) noexcept + { + return a.family () == b.family (); + } + + /** + * @brief check if not equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if not equals. + */ + constexpr bool operator!= (const Dns& a, const Dns& b) noexcept + { + return !(a == b); + } +} + +#endif diff --git a/fabric/include/join/dot.hpp b/fabric/include/join/dot.hpp new file mode 100644 index 00000000..33bc7ea8 --- /dev/null +++ b/fabric/include/join/dot.hpp @@ -0,0 +1,136 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_FABRIC_DOT_HPP +#define JOIN_FABRIC_DOT_HPP + +// libjoin. +#include + +namespace join +{ + template + class BasicTlsResolver; + + /** + * @brief DNS over TLS protocol class. + */ + class Dot + { + public: + using Endpoint = BasicInternetEndpoint; + using Socket = TlsWrapper>; + using Resolver = BasicTlsResolver; + + /** + * @brief construct the DoT protocol instance. + * @param family IP address family. + */ + constexpr Dot (int family = AF_INET) noexcept + : _family (family) + { + } + + /** + * @brief get protocol suitable for IPv4 address family. + * @return an IPv4 address family suitable protocol. + */ + static inline Dot& v4 () noexcept + { + static Dot dotv4 (AF_INET); + return dotv4; + } + + /** + * @brief get protocol suitable for IPv6 address family. + * @return an IPv6 address family suitable protocol. + */ + static inline Dot& v6 () noexcept + { + static Dot dotv6 (AF_INET6); + return dotv6; + } + + /** + * @brief get the protocol IP address family. + * @return the protocol IP address family. + */ + constexpr int family () const noexcept + { + return _family; + } + + /** + * @brief get the protocol communication semantic. + * @return the protocol communication semantic. + */ + constexpr int type () const noexcept + { + return SOCK_STREAM; + } + + /** + * @brief get the protocol type. + * @return the protocol type. + */ + constexpr int protocol () const noexcept + { + return IPPROTO_TCP; + } + + /// default DoT port. + static constexpr uint16_t defaultPort = 853; + + /// maximum DoT message size. + static constexpr size_t maxMsgSize = 16384; + + private: + /// IP address family. + int _family; + }; + + /** + * @brief check if equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if equals. + */ + constexpr bool operator== (const Dot& a, const Dot& b) noexcept + { + return a.family () == b.family (); + } + + /** + * @brief check if not equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if not equals. + */ + constexpr bool operator!= (const Dot& a, const Dot& b) noexcept + { + return !(a == b); + } +} + +#endif diff --git a/fabric/include/join/mdns.hpp b/fabric/include/join/mdns.hpp new file mode 100644 index 00000000..57505a96 --- /dev/null +++ b/fabric/include/join/mdns.hpp @@ -0,0 +1,146 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_FABRIC_MDNS_HPP +#define JOIN_FABRIC_MDNS_HPP + +// libjoin. +#include + +namespace join +{ + template + class BasicDatagramPeer; + + /** + * @brief Multicast DNS protocol class + */ + class Mdns + { + public: + using Endpoint = BasicInternetEndpoint; + using Socket = BasicDatagramSocket; + using Peer = BasicDatagramPeer; + + /** + * @brief construct the mDNS protocol instance. + * @param family IP address family. + */ + constexpr Mdns (int family = AF_INET) noexcept + : _family (family) + { + } + + /** + * @brief get protocol suitable for IPv4 address family. + * @return an IPv4 address family suitable protocol. + */ + static inline Mdns& v4 () noexcept + { + static Mdns mdnsv4 (AF_INET); + return mdnsv4; + } + + /** + * @brief get protocol suitable for IPv6 address family. + * @return an IPv6 address family suitable protocol. + */ + static inline Mdns& v6 () noexcept + { + static Mdns mdnsv6 (AF_INET6); + return mdnsv6; + } + + /** + * @brief get the protocol IP address family. + * @return the protocol IP address family. + */ + constexpr int family () const noexcept + { + return _family; + } + + /** + * @brief get the protocol communication semantic. + * @return the protocol communication semantic. + */ + constexpr int type () const noexcept + { + return SOCK_DGRAM; + } + + /** + * @brief get the protocol type. + * @return the protocol type. + */ + constexpr int protocol () const noexcept + { + return IPPROTO_UDP; + } + + /** + * @brief get multicast address for the given address family. + * @param family IP address family. + * @return multicast IP address. + */ + static IpAddress multicastAddress (int family) noexcept + { + return (family == AF_INET6) ? "ff02::fb" : "224.0.0.251"; + } + + /// default DNS port. + static constexpr uint16_t defaultPort = 5353; + + /// maximum DNS message size. + static constexpr size_t maxMsgSize = 8192; + + private: + /// IP address family. + int _family; + }; + + /** + * @brief check if equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if equals. + */ + constexpr bool operator== (const Mdns& a, const Mdns& b) noexcept + { + return a.family () == b.family (); + } + + /** + * @brief check if not equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if not equals. + */ + constexpr bool operator!= (const Mdns& a, const Mdns& b) noexcept + { + return !(a == b); + } +} + +#endif diff --git a/fabric/include/join/nameserver.hpp b/fabric/include/join/nameserver.hpp index 50514c39..ac02cfae 100644 --- a/fabric/include/join/nameserver.hpp +++ b/fabric/include/join/nameserver.hpp @@ -29,7 +29,8 @@ #include #include #include -#include +#include +#include namespace join { @@ -37,7 +38,7 @@ namespace join * @brief basic DNS name server over datagram socket. */ template - class BasicDatagramNameServer : public Protocol::Socket, public EventHandler + class BasicDatagramNameServer : public EventHandler { public: using Socket = typename Protocol::Socket; @@ -48,8 +49,7 @@ namespace join * @param reactor event loop reactor. */ explicit BasicDatagramNameServer (Reactor& reactor = ReactorThread::reactor ()) - : Socket () - , _reactor (reactor) + : _reactor (reactor) , _buffer (std::make_unique (Protocol::maxMsgSize)) { } @@ -90,14 +90,14 @@ namespace join * @param endpoint endpoint to bind to. * @return 0 on success, -1 on failure. */ - virtual int bind (const Endpoint& endpoint) noexcept override + virtual int bind (const Endpoint& endpoint) noexcept { - if (Socket::bind (endpoint) == -1) + if (_socket.bind (endpoint) == -1) { return -1; // LCOV_EXCL_LINE } - _reactor.addHandler (this->handle (), this); + _reactor.addHandler (_socket.handle (), this); return 0; } @@ -105,10 +105,10 @@ namespace join /** * @brief close the socket and unregister from the reactor. */ - virtual void close () noexcept override + virtual void close () noexcept { - _reactor.delHandler (this->handle ()); - Socket::close (); + _reactor.delHandler (_socket.handle ()); + _socket.close (); } /** @@ -152,7 +152,7 @@ namespace join virtual void onReadable ([[maybe_unused]] int fd) override { Endpoint from; - int size = this->readFrom (_buffer.get (), Protocol::maxMsgSize, &from); + int size = _socket.readFrom (_buffer.get (), Protocol::maxMsgSize, &from); if (size >= int (_headerSize)) { std::stringstream data; @@ -161,7 +161,7 @@ namespace join DnsPacket packet; _message.deserialize (packet, data); packet.src = from.ip (); - packet.dest = this->localEndpoint ().ip (); + packet.dest = _socket.localEndpoint ().ip (); packet.port = from.port (); if ((packet.flags & 0x8000) == 0) @@ -196,7 +196,7 @@ namespace join // LCOV_EXCL_STOP } - if (this->writeTo (buffer.data (), buffer.size (), {packet.dest, packet.port}) == -1) + if (_socket.writeTo (buffer.data (), buffer.size (), {packet.dest, packet.port}) == -1) { return -1; // LCOV_EXCL_LINE } @@ -210,6 +210,9 @@ namespace join /// DNS message codec. DnsMessage _message; + /// underlying socket. + Socket _socket; + /// event loop reactor. Reactor& _reactor; @@ -307,12 +310,12 @@ namespace join IpAddress maddress = Protocol::multicastAddress (family); Endpoint endpoint{IpAddress (family), Protocol::defaultPort}; - if ((this->_state == Socket::State::Closed) && (this->open (endpoint.protocol ()) == -1)) + if ((this->_socket.opened () == false) && (this->_socket.open (endpoint.protocol ()) == -1)) { return -1; // LCOV_EXCL_LINE } - if (this->setOption (Socket::ReusePort, 1) == -1) + if (this->_socket.setOption (Socket::ReusePort, 1) == -1) { // LCOV_EXCL_START this->close (); @@ -320,7 +323,7 @@ namespace join // LCOV_EXCL_STOP } - if (Socket::bind (endpoint) == -1) + if (this->_socket.bind (endpoint) == -1) { // LCOV_EXCL_START this->close (); @@ -333,7 +336,8 @@ namespace join ipv6_mreq mreq{}; ::memcpy (&mreq.ipv6mr_multiaddr, maddress.addr (), maddress.length ()); mreq.ipv6mr_interface = _ifindex; - if (::setsockopt (this->handle (), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1) + if (::setsockopt (this->_socket.handle (), IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == + -1) { // LCOV_EXCL_START lastError = std::error_code (errno, std::generic_category ()); @@ -341,7 +345,8 @@ namespace join return -1; // LCOV_EXCL_STOP } - if (::setsockopt (this->handle (), IPPROTO_IPV6, IPV6_MULTICAST_IF, &_ifindex, sizeof (_ifindex)) == -1) + if (::setsockopt (this->_socket.handle (), IPPROTO_IPV6, IPV6_MULTICAST_IF, &_ifindex, + sizeof (_ifindex)) == -1) { // LCOV_EXCL_START lastError = std::error_code (errno, std::generic_category ()); @@ -356,13 +361,13 @@ namespace join ip_mreqn mreq{}; ::memcpy (&mreq.imr_multiaddr, maddress.addr (), maddress.length ()); mreq.imr_ifindex = static_cast (_ifindex); - if (::setsockopt (this->handle (), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1) + if (::setsockopt (this->_socket.handle (), IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1) { lastError = std::error_code (errno, std::generic_category ()); this->close (); return -1; } - if (::setsockopt (this->handle (), IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof (mreq)) == -1) + if (::setsockopt (this->_socket.handle (), IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof (mreq)) == -1) { lastError = std::error_code (errno, std::generic_category ()); this->close (); @@ -381,7 +386,7 @@ namespace join } #endif - this->_reactor.addHandler (this->handle (), this); + this->_reactor.addHandler (this->_socket.handle (), this); return 0; } @@ -402,7 +407,7 @@ namespace join DnsPacket packet{}; packet.id = 0; packet.flags = 0; - IpAddress mcast = Protocol::multicastAddress (this->family ()); + IpAddress mcast = Protocol::multicastAddress (this->_socket.family ()); packet.dest = IpAddress (mcast.addr (), mcast.length (), _ifindex); packet.port = Protocol::defaultPort; @@ -436,7 +441,7 @@ namespace join DnsPacket packet{}; packet.id = 0; packet.flags = (uint16_t (1) << 15) | (uint16_t (1) << 10); - IpAddress mcast = Protocol::multicastAddress (this->family ()); + IpAddress mcast = Protocol::multicastAddress (this->_socket.family ()); packet.dest = IpAddress (mcast.addr (), mcast.length (), _ifindex); packet.port = Protocol::defaultPort; @@ -464,7 +469,7 @@ namespace join DnsPacket packet{}; packet.id = 0; packet.flags = (uint16_t (1) << 15) | (uint16_t (1) << 10); - IpAddress mcast = Protocol::multicastAddress (this->family ()); + IpAddress mcast = Protocol::multicastAddress (this->_socket.family ()); packet.dest = IpAddress (mcast.addr (), mcast.length (), _ifindex); packet.port = Protocol::defaultPort; @@ -494,7 +499,7 @@ namespace join DnsPacket packet{}; packet.id = 0; packet.flags = 0; - IpAddress mcast = Protocol::multicastAddress (this->family ()); + IpAddress mcast = Protocol::multicastAddress (this->_socket.family ()); packet.dest = IpAddress (mcast.addr (), mcast.length (), _ifindex); packet.port = Protocol::defaultPort; @@ -676,7 +681,7 @@ namespace join void onReadable ([[maybe_unused]] int fd) override final { Endpoint from; - int size = this->readFrom (this->_buffer.get (), Protocol::maxMsgSize, &from); + int size = this->_socket.readFrom (this->_buffer.get (), Protocol::maxMsgSize, &from); if (size >= int (this->_headerSize)) { std::stringstream data; @@ -684,7 +689,7 @@ namespace join DnsPacket packet; this->_message.deserialize (packet, data); - IpAddress mcast = Protocol::multicastAddress (this->family ()); + IpAddress mcast = Protocol::multicastAddress (this->_socket.family ()); packet.src = from.ip (); packet.dest = IpAddress (mcast.addr (), mcast.length (), _ifindex); packet.port = from.port (); @@ -816,7 +821,7 @@ namespace join */ int query (DnsPacket& packet, std::chrono::milliseconds timeout) { - IpAddress mcast = Protocol::multicastAddress (this->family ()); + IpAddress mcast = Protocol::multicastAddress (this->_socket.family ()); packet.dest = IpAddress (mcast.addr (), mcast.length (), _ifindex); packet.port = Protocol::defaultPort; @@ -850,7 +855,7 @@ namespace join // LCOV_EXCL_STOP } - if (this->writeTo (buffer.data (), buffer.size (), {packet.dest, packet.port}) == -1) + if (this->_socket.writeTo (buffer.data (), buffer.size (), {packet.dest, packet.port}) == -1) { // LCOV_EXCL_START _pending.erase (inserted.first); diff --git a/fabric/include/join/netlink.hpp b/fabric/include/join/netlink.hpp new file mode 100644 index 00000000..5d5c2a77 --- /dev/null +++ b/fabric/include/join/netlink.hpp @@ -0,0 +1,127 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_FABRIC_NETLINK_HPP +#define JOIN_FABRIC_NETLINK_HPP + +// libjoin. +#include +#include + +namespace join +{ + /** + * @brief netlink protocol class. + */ + class Netlink + { + public: + using Endpoint = BasicNetlinkEndpoint; + using Socket = BasicDatagramSocket; + + /** + * @brief construct the netlink protocol instance by default. + * @param proto protocol type. + */ + constexpr Netlink (int proto = NETLINK_ROUTE) noexcept + : _proto (proto) + { + } + + /** + * @brief get protocol suitable for netlink route. + * @return a netlink route protocol. + */ + static inline Netlink& rt () noexcept + { + static Netlink route (NETLINK_ROUTE); + return route; + } + + /** + * @brief get protocol suitable for netlink netfilter. + * @return a netlink route protocol. + */ + static inline Netlink& nf () noexcept + { + static Netlink netfilter (NETLINK_NETFILTER); + return netfilter; + } + + /** + * @brief get the protocol address family. + * @return the protocol address family. + */ + constexpr int family () const noexcept + { + return AF_NETLINK; + } + + /** + * @brief get the protocol communication semantic. + * @return the protocol communication semantic. + */ + constexpr int type () const noexcept + { + return SOCK_RAW; + } + + /** + * @brief get the protocol type. + * @return the protocol type. + */ + constexpr int protocol () const noexcept + { + return _proto; + } + + private: + /// protocol. + int _proto; + }; + + /** + * @brief check if equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if equals. + */ + constexpr bool operator== (const Netlink& a, const Netlink& b) noexcept + { + return a.protocol () == b.protocol (); + } + + /** + * @brief check if not equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if not equals. + */ + constexpr bool operator!= (const Netlink& a, const Netlink& b) noexcept + { + return !(a == b); + } +} + +#endif diff --git a/fabric/include/join/netlink_endpoint.hpp b/fabric/include/join/netlink_endpoint.hpp new file mode 100644 index 00000000..d4169d91 --- /dev/null +++ b/fabric/include/join/netlink_endpoint.hpp @@ -0,0 +1,271 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_FABRIC_NETLINK_ENDPOINT_HPP +#define JOIN_FABRIC_NETLINK_ENDPOINT_HPP + +// libjoin. +#include + +// C. +#include +#include +#include + +namespace join +{ + /** + * @brief basic netlink endpoint class. + */ + template + class BasicNetlinkEndpoint : public BasicEndpoint + { + public: + /** + * @brief default constructor. + */ + constexpr BasicNetlinkEndpoint () noexcept + : BasicEndpoint () + , _protocol (Protocol ().protocol ()) + { + } + + /** + * @brief create instance using socket address. + * @param addr socket address. + * @param len socket address length. + */ + BasicNetlinkEndpoint (const struct sockaddr* addr, socklen_t len) noexcept + : BasicEndpoint (addr, len) + , _protocol (Protocol ().protocol ()) + { + } + + /** + * @brief create instance using netlink groups. + * @param protocol netlink protocol. + * @param pid process id. + * @param groups netlink groups to set. + */ + BasicNetlinkEndpoint (const Protocol& protocol, uint32_t pid, uint32_t groups) noexcept + : BasicEndpoint () + , _protocol (protocol.protocol ()) + { + struct sockaddr_nl* nl = reinterpret_cast (&this->_addr); + nl->nl_pid = pid; + nl->nl_groups = groups; + } + + /** + * @brief create instance using netlink groups. + * @param pid process id. + * @param groups netlink groups to set. + */ + BasicNetlinkEndpoint (uint32_t pid, uint32_t groups) noexcept + : BasicNetlinkEndpoint (Protocol (), pid, groups) + { + } + + /** + * @brief create instance using netlink groups. + * @param protocol netlink protocol. + * @param groups netlink groups to set. + */ + BasicNetlinkEndpoint (const Protocol& protocol, uint32_t groups) noexcept + : BasicNetlinkEndpoint (protocol, getpid (), groups) + { + } + + /** + * @brief create instance using netlink groups. + * @param groups netlink groups to set. + */ + BasicNetlinkEndpoint (uint32_t groups) noexcept + : BasicNetlinkEndpoint (Protocol (), getpid (), groups) + { + } + + /** + * @brief get endpoint protocol. + * @return endpoint protocol. + */ + Protocol protocol () const noexcept + { + if (_protocol == NETLINK_NETFILTER) + { + return Protocol::nf (); + } + return Protocol::rt (); + } + + /** + * @brief get socket address length. + * @return socket address length. + */ + constexpr socklen_t length () const noexcept + { + return sizeof (struct sockaddr_nl); + } + + /** + * @brief set process id. + * @param pid process id. + */ + void pid (uint32_t pid) noexcept + { + reinterpret_cast (&this->_addr)->nl_pid = pid; + } + + /** + * @brief get process id. + * @return process id. + */ + uint32_t pid () const noexcept + { + return reinterpret_cast (&this->_addr)->nl_pid; + } + + /** + * @brief set netlink groups. + * @param groups netlink groups bitmask. + */ + void groups (uint32_t groups) noexcept + { + reinterpret_cast (&this->_addr)->nl_groups = groups; + } + + /** + * @brief get netlink groups. + * @return netlink groups bitmask. + */ + uint32_t groups () const noexcept + { + return reinterpret_cast (&this->_addr)->nl_groups; + } + + /** + * @brief get device name (not applicable for netlink). + * @return empty string. + */ + std::string device () const + { + return std::string (); + } + + protected: + /// netlink protocol type. + int _protocol; + }; + + /** + * @brief compare if endpoints are equal. + * @param a endpoint to compare. + * @param b endpoint to compare to. + * @return true if endpoints are equal, false otherwise. + */ + template + bool operator== (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept + { + return a.pid () == b.pid () && a.groups () == b.groups (); + } + + /** + * @brief compare if endpoints are not equal. + * @param a endpoint to compare. + * @param b endpoint to compare to. + * @return true if endpoints are not equal, false otherwise. + */ + template + bool operator!= (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept + { + return !(a == b); + } + + /** + * @brief compare if endpoint is lower. + * @param a endpoint to compare. + * @param b endpoint to compare to. + * @return true if lower, false otherwise. + */ + template + bool operator< (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept + { + if (a.pid () != b.pid ()) + { + return a.pid () < b.pid (); + } + return a.groups () < b.groups (); + } + + /** + * @brief compare if endpoint is greater. + * @param a endpoint to compare. + * @param b endpoint to compare to. + * @return true if greater, false otherwise. + */ + template + bool operator> (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept + { + return b < a; + } + + /** + * @brief compare if endpoint is lower or equal. + * @param a endpoint to compare. + * @param b endpoint to compare to. + * @return true if lower or equal, false otherwise. + */ + template + bool operator<= (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept + { + return !(b < a); + } + + /** + * @brief compare if endpoint is greater or equal. + * @param a endpoint to compare. + * @param b endpoint to compare to. + * @return true if greater or equal, false otherwise. + */ + template + bool operator>= (const BasicNetlinkEndpoint& a, const BasicNetlinkEndpoint& b) noexcept + { + return !(a < b); + } + + /** + * @brief push endpoint representation into a stream. + * @param os output stream. + * @param endpoint endpoint to push. + * @return output stream. + */ + template + std::ostream& operator<< (std::ostream& os, const BasicNetlinkEndpoint& endpoint) + { + os << "pid=" << endpoint.pid () << ",groups=" << endpoint.groups (); + return os; + } +} + +#endif diff --git a/fabric/include/join/netlinkmanager.hpp b/fabric/include/join/netlinkmanager.hpp index 8ea5ee33..374dd876 100644 --- a/fabric/include/join/netlinkmanager.hpp +++ b/fabric/include/join/netlinkmanager.hpp @@ -27,8 +27,8 @@ // libjoin. #include +#include #include -#include #include // C++. @@ -38,8 +38,6 @@ #include // C. -#include -#include #include #include diff --git a/fabric/include/join/resolver.hpp b/fabric/include/join/resolver.hpp index caa2eb59..1891e94d 100644 --- a/fabric/include/join/resolver.hpp +++ b/fabric/include/join/resolver.hpp @@ -29,7 +29,8 @@ #include #include #include -#include +#include +#include // C++. #include @@ -48,12 +49,11 @@ namespace join * @brief basic DNS resolver over datagram socket. */ template - class BasicDatagramResolver : public Protocol::Socket, public EventHandler + class BasicDatagramResolver : public EventHandler { public: using Socket = typename Protocol::Socket; using Endpoint = typename Protocol::Endpoint; - using State = typename Socket::State; /// notification callback definition. using DnsNotify = std::function; @@ -64,22 +64,29 @@ namespace join /// callback called when a lookup sequence failed. DnsNotify onFailure; + explicit BasicDatagramResolver (const std::string& server = {}, uint16_t port = Protocol::defaultPort, + Reactor& reactor = ReactorThread::reactor ()) + : BasicDatagramResolver (Socket{}, server, port, reactor) + { + } + /** * @brief construct the resolver instance. * @param server remote DNS server hostname or IP address. * @param port remote DNS server port. * @param reactor reactor instance. */ - explicit BasicDatagramResolver (const std::string& server = {}, uint16_t port = Protocol::defaultPort, + explicit BasicDatagramResolver (Socket&& socket, const std::string& server = {}, + uint16_t port = Protocol::defaultPort, Reactor& reactor = ReactorThread::reactor ()) - : Socket () #ifdef DEBUG - , onSuccess (defaultOnSuccess) + : onSuccess (defaultOnSuccess) , onFailure (defaultOnFailure) #else - , onSuccess (nullptr) + : onSuccess (nullptr) , onFailure (nullptr) #endif + , _socket (std::move (socket)) , _server (server) , _port (port) , _reactor (reactor) @@ -116,46 +123,9 @@ namespace join /** * @brief destroy instance. */ - virtual ~BasicDatagramResolver () noexcept = default; - - /** - * @brief make a connection to the given endpoint. - * @param endpoint endpoint to connect to. - * @return 0 on success, -1 on failure. - */ - virtual int connect (const Endpoint& endpoint) override + virtual ~BasicDatagramResolver () noexcept { - if (Socket::connect (endpoint) == -1) - { - return -1; - } - - _server = endpoint.hostname (); - if (_server.empty ()) - { - _server = endpoint.ip ().toString (); - } - _port = endpoint.port (); - - this->_reactor.addHandler (this->handle (), this); - - return 0; - } - - /** - * @brief shutdown the connection. - * @return 0 on success, -1 on failure. - */ - virtual int disconnect () override - { - this->_reactor.delHandler (this->_handle); - - if (Socket::disconnect () == -1) - { - return -1; - } - - return 0; + disconnect (); } /** @@ -713,13 +683,53 @@ namespace join } protected: + /** + * @brief make a connection to the given endpoint. + * @param endpoint endpoint to connect to. + * @param timeout timeout in milliseconds. + * @return 0 on success, -1 on failure. + */ + virtual int connect (const Endpoint& endpoint, + [[maybe_unused]] std::chrono::milliseconds timeout = std::chrono::seconds (5)) noexcept + { + if (_socket.connect (endpoint) == -1) + { + _socket.close (); + return -1; + } + + _server = endpoint.hostname ().empty () ? endpoint.ip ().toString () : endpoint.hostname (); + _port = endpoint.port (); + + _reactor.addHandler (_socket.handle (), this); + + return 0; + } + + /** + * @brief shutdown the connection. + * @return 0 on success, -1 on failure. + */ + virtual int disconnect ([[maybe_unused]] std::chrono::milliseconds timeout = std::chrono::seconds (5)) noexcept + { + _reactor.delHandler (_socket.handle ()); + + if (_socket.disconnect () == -1) + { + _socket.close (); + return -1; + } + + return 0; + } + /** * @brief check if client must reconnect. * @return true if reconnection is required. */ - bool needReconnection () noexcept + virtual bool needReconnection () noexcept { - return !this->connected (); + return !_socket.connected () || _socket.remoteEndpoint ().ip ().isWildcard (); } /** @@ -728,23 +738,32 @@ namespace join * @param timeout timeout in milliseconds. * @return 0 on success, -1 on failure. */ - virtual int reconnect (const Endpoint& endpoint, [[maybe_unused]] std::chrono::milliseconds timeout) + int reconnect (const Endpoint& endpoint, std::chrono::milliseconds timeout = std::chrono::seconds (5)) { - if (this->disconnect () == -1) - { - // LCOV_EXCL_START - this->close (); - return -1; - // LCOV_EXCL_STOP - } + disconnect (timeout); + return connect (endpoint, timeout); + } - if (this->connect (endpoint) == -1) - { - this->close (); - return -1; - } + /** + * @brief read a DNS message. + * @param data destination buffer. + * @param maxSize maximum number of bytes to read. + * @return number of bytes read, or -1 on error. + */ + virtual int read (char* data, unsigned long maxSize) noexcept + { + return _socket.read (data, maxSize); + } - return 0; + /** + * @brief write a DNS message. + * @param data source buffer. + * @param size number of bytes to write. + * @return number of bytes written, or -1 on error. + */ + virtual int write (const char* data, unsigned long size) noexcept + { + return _socket.write (data, size); } /** @@ -755,7 +774,9 @@ namespace join */ int query (DnsPacket& packet, std::chrono::milliseconds timeout) { - if (this->_remote.ip ().isWildcard ()) + auto remote = _socket.remoteEndpoint (); + + if (remote.ip ().isWildcard ()) { IpAddress ip = IpAddress::isIpAddress (_server) ? IpAddress (_server) : Dns::Resolver::lookupAddress (_server, AF_INET); @@ -767,26 +788,26 @@ namespace join return -1; } - this->_remote.ip (ip); - this->_remote.port (_port); + remote.ip (ip); + remote.port (_port); } - packet.dest = this->_remote.ip (); - packet.port = this->_remote.port (); + packet.dest = remote.ip (); + packet.port = remote.port (); - if (this->needReconnection ()) + if (needReconnection ()) { Endpoint endpoint{packet.dest, packet.port}; endpoint.hostname (_server); - if (this->reconnect (endpoint, timeout) == -1) + if (reconnect (endpoint, timeout) == -1) { notify (onFailure, packet); return -1; } } - packet.src = this->localEndpoint ().ip (); + packet.src = _socket.localEndpoint ().ip (); std::stringstream data; if (_message.serialize (packet, data) == -1) @@ -816,7 +837,7 @@ namespace join // LCOV_EXCL_STOP } - if (this->write (buffer.data (), buffer.size ()) == -1) + if (write (buffer.data (), buffer.size ()) == -1) { // LCOV_EXCL_START _pending.erase (inserted.first); @@ -855,7 +876,7 @@ namespace join */ void onReadable ([[maybe_unused]] int fd) override final { - int size = this->read (_buffer.get (), Protocol::maxMsgSize); + int size = read (_buffer.get (), Protocol::maxMsgSize); if (size >= int (_headerSize)) { std::stringstream data; @@ -863,9 +884,9 @@ namespace join DnsPacket packet; _message.deserialize (packet, data); - packet.src = this->localEndpoint ().ip (); - packet.dest = this->remoteEndpoint ().ip (); - packet.port = this->remoteEndpoint ().port (); + packet.src = _socket.localEndpoint ().ip (); + packet.dest = _socket.remoteEndpoint ().ip (); + packet.port = _socket.remoteEndpoint ().port (); if (packet.flags & 0x8000) { @@ -892,7 +913,7 @@ namespace join */ void onClose ([[maybe_unused]] int fd) override final { - this->disconnect (); + disconnect (); } #ifdef DEBUG @@ -1005,6 +1026,9 @@ namespace join /// DNS message codec. DnsMessage _message; + /// underlying socket. + Socket _socket; + /// remote DNS server. std::string _server; @@ -1040,8 +1064,8 @@ namespace join { public: using Socket = typename Protocol::Socket; + using UnderlyingSocket = typename Socket::UnderlyingSocket; using Endpoint = typename Protocol::Endpoint; - using State = typename Socket::State; /** * @brief construct the DoT resolver instance. @@ -1049,9 +1073,9 @@ namespace join * @param port remote DNS server port. * @param reactor reactor instance. */ - explicit BasicTlsResolver (const std::string& server = {}, uint16_t port = Protocol::defaultPort, - Reactor& reactor = ReactorThread::reactor ()) - : BasicDatagramResolver (server, port, reactor) + explicit BasicTlsResolver (TlsContext ctx, const std::string& server = {}, + uint16_t port = Protocol::defaultPort, Reactor& reactor = ReactorThread::reactor ()) + : BasicDatagramResolver (Socket (UnderlyingSocket{}, ctx), server, port, reactor) { } @@ -1087,91 +1111,187 @@ namespace join virtual ~BasicTlsResolver () noexcept = default; /** - * @brief make a connection to the given endpoint. - * @param endpoint endpoint to connect to. - * @return 0 on success, -1 on failure. + * @brief resolve host name using system name servers and return all IP addresses found. + * @param host host name to resolve. + * @param family address family. + * @return the resolved IP address list, empty on error. */ - virtual int connect (const Endpoint& endpoint) override - { - if (Socket::connect (endpoint) == -1) - { - return -1; - } + static IpAddressList lookupAllAddress (const std::string& host, int family) = delete; - this->_server = this->_remote.hostname (); - if (this->_server.empty ()) - { - this->_server = this->_remote.ip ().toString (); - } - this->_port = this->_remote.port (); + /** + * @brief resolve host name using system name servers and return all IP addresses found. + * @param host host name to resolve. + * @return the resolved IP address list, empty on error. + */ + static IpAddressList lookupAllAddress (const std::string& host) = delete; - return 0; - } + /** + * @brief resolve host name using system name servers. + * @param host host name to resolve. + * @param family address family. + * @return the first resolved IP address found, wildcard address on error. + */ + static IpAddress lookupAddress (const std::string& host, int family) = delete; + + /** + * @brief resolve host name using system name servers. + * @param host host name to resolve. + * @return the first resolved IP address found, wildcard address on error. + */ + static IpAddress lookupAddress (const std::string& host) = delete; + + /** + * @brief resolve all host address. + * @param address host address to resolve. + * @return the resolved alias list. + */ + static AliasList lookupAllName (const IpAddress& address) = delete; + + /** + * @brief resolve host address. + * @param address host address to resolve. + * @return the first resolved alias. + */ + static std::string lookupName (const IpAddress& address) = delete; + + /** + * @brief resolve all host name server. + * @param host host name to resolve. + * @return the resolved name server list. + */ + static ServerList lookupAllNameServer (const std::string& host) = delete; + + /** + * @brief resolve host name server. + * @param host host name to resolve. + * @return the first resolved name server. + */ + static std::string lookupNameServer (const std::string& host) = delete; + /** + * @brief resolve host start of authority name server. + * @param host host name to resolve. + * @return the start of authority name server. + */ + static std::string lookupAuthority (const std::string& host) = delete; + + /** + * @brief resolve all host mail exchanger. + * @param host host name to resolve. + * @return the resolved mail exchanger list. + */ + static ExchangerList lookupAllMailExchanger (const std::string& host) = delete; + + /** + * @brief resolve host mail exchanger. + * @param host host name to resolve. + * @return the first resolved mail exchanger. + */ + static std::string lookupMailExchanger (const std::string& host) = delete; + + private: /** * @brief make an encrypted connection to the given endpoint. * @param endpoint endpoint to connect to. + * @param timeout timeout in milliseconds. * @return 0 on success, -1 on failure. */ - virtual int connectEncrypted (const Endpoint& endpoint) override + int connect (const Endpoint& endpoint, + std::chrono::milliseconds timeout = std::chrono::seconds (5)) noexcept override final { - if (Socket::connectEncrypted (endpoint) == -1) + if (this->_socket.connect (endpoint) == -1) { - return -1; - } + if (lastError != Errc::TemporaryError) + { + close (); + return -1; + } - this->_reactor.addHandler (this->handle (), this); + if (!this->_socket.waitConnected (timeout.count ())) + { + close (); + return -1; + } + } - return 0; - } + this->_server = endpoint.hostname ().empty () ? endpoint.ip ().toString () : endpoint.hostname (); + this->_port = endpoint.port (); - /** - * @brief wait until TLS handshake is performed or timeout occur (non blocking socket). - * @param timeout timeout in milliseconds (0: infinite). - * return true on success, false otherwise. - */ - virtual bool waitEncrypted (int timeout = 0) override - { - if (!Socket::waitEncrypted (timeout)) + if (this->_socket.handshake () == -1) { - return false; + if (lastError != Errc::TemporaryError) + { + close (); + return -1; + } + + if (!this->_socket.waitHandshake (timeout.count ())) + { + close (); + return -1; + } } - this->_reactor.addHandler (this->handle (), this); + // if (!this->_socket.waitHandshake (timeout.count ())) + // { + // close (); + // return -1; + // } + + this->_reactor.addHandler (this->_socket.handle (), this); - return true; + return 0; } /** - * @brief block until connected. - * @param timeout timeout in milliseconds. - * @return true if connected, false otherwise. + * @brief shutdown the encrypted connection. + * @return 0 on success, -1 on failure. */ - virtual bool waitConnected (int timeout = 0) override + int disconnect (std::chrono::milliseconds timeout = std::chrono::seconds (5)) noexcept override final { - if (!Socket::waitConnected (timeout)) + this->_reactor.delHandler (this->_socket.handle ()); + + if (this->_socket.shutdown () == -1) { - return false; + if ((lastError != Errc::TemporaryError)) + { + close (); + return -1; + } + + if (!this->_socket.waitShutdown (timeout.count ())) + { + close (); + return -1; + } } - this->_server = this->_remote.hostname (); - if (this->_server.empty ()) + if (!this->_socket.waitDisconnected (timeout.count ())) { - this->_server = this->_remote.ip ().toString (); + close (); + return -1; } - this->_port = this->_remote.port (); - return true; + return 0; } /** * @brief close the TLS connection and reset framing state. */ - virtual void close () noexcept override + void close () noexcept { - Socket::close (); - _size = 0; + this->_socket.close (); _offset = 0; + _size = 0; + } + + /** + * @brief check if client must reconnect. + * @return true if reconnection is required. + */ + bool needReconnection () noexcept override final + { + return !this->_socket.encrypted () || this->_socket.remoteEndpoint ().ip ().isWildcard (); } /** @@ -1180,11 +1300,11 @@ namespace join * @param maxSize maximum number of bytes to read. * @return number of bytes read, or -1 on error. */ - virtual int read (char* data, unsigned long maxSize) noexcept override + int read (char* data, unsigned long maxSize) noexcept override final { if (_offset < _frameHeaderSize) { - int nread = Socket::read (data + _offset, _frameHeaderSize - _offset); + int nread = this->_socket.read (data + _offset, _frameHeaderSize - _offset); if (nread == -1) { if (lastError != Errc::TemporaryError) @@ -1214,7 +1334,7 @@ namespace join } } - int nread = Socket::read (data + (_offset - _frameHeaderSize), _size - (_offset - _frameHeaderSize)); + int nread = this->_socket.read (data + (_offset - _frameHeaderSize), _size - (_offset - _frameHeaderSize)); if (nread == -1) { if (lastError != Errc::TemporaryError) @@ -1246,7 +1366,7 @@ namespace join * @param size number of bytes to write. * @return number of bytes written, or -1 on error. */ - virtual int write (const char* data, unsigned long size) noexcept override + int write (const char* data, unsigned long size) noexcept override final { uint16_t msgLength = htons (static_cast (size)); const char* p = reinterpret_cast (&msgLength); @@ -1254,12 +1374,12 @@ namespace join while (remaining > 0) { - int result = Socket::write (p, remaining); + int result = this->_socket.write (p, remaining); if (result == -1) { if (lastError == Errc::TemporaryError) { - if (this->waitReadyWrite ()) + if (this->_socket.waitReadyWrite ()) continue; } return -1; @@ -1273,12 +1393,12 @@ namespace join while (remaining > 0) { - int result = Socket::write (p, remaining); + int result = this->_socket.write (p, remaining); if (result == -1) { if (lastError == Errc::TemporaryError) { - if (this->waitReadyWrite ()) + if (this->_socket.waitReadyWrite ()) continue; } return -1; @@ -1290,132 +1410,7 @@ namespace join return static_cast (size); } - /** - * @brief resolve host name using system name servers and return all IP addresses found. - * @param host host name to resolve. - * @param family address family. - * @return the resolved IP address list, empty on error. - */ - static IpAddressList lookupAllAddress (const std::string& host, int family) = delete; - - /** - * @brief resolve host name using system name servers and return all IP addresses found. - * @param host host name to resolve. - * @return the resolved IP address list, empty on error. - */ - static IpAddressList lookupAllAddress (const std::string& host) = delete; - - /** - * @brief resolve host name using system name servers. - * @param host host name to resolve. - * @param family address family. - * @return the first resolved IP address found, wildcard address on error. - */ - static IpAddress lookupAddress (const std::string& host, int family) = delete; - - /** - * @brief resolve host name using system name servers. - * @param host host name to resolve. - * @return the first resolved IP address found, wildcard address on error. - */ - static IpAddress lookupAddress (const std::string& host) = delete; - - /** - * @brief resolve all host address. - * @param address host address to resolve. - * @return the resolved alias list. - */ - static AliasList lookupAllName (const IpAddress& address) = delete; - - /** - * @brief resolve host address. - * @param address host address to resolve. - * @return the first resolved alias. - */ - static std::string lookupName (const IpAddress& address) = delete; - - /** - * @brief resolve all host name server. - * @param host host name to resolve. - * @return the resolved name server list. - */ - static ServerList lookupAllNameServer (const std::string& host) = delete; - - /** - * @brief resolve host name server. - * @param host host name to resolve. - * @return the first resolved name server. - */ - static std::string lookupNameServer (const std::string& host) = delete; - - /** - * @brief resolve host start of authority name server. - * @param host host name to resolve. - * @return the start of authority name server. - */ - static std::string lookupAuthority (const std::string& host) = delete; - - /** - * @brief resolve all host mail exchanger. - * @param host host name to resolve. - * @return the resolved mail exchanger list. - */ - static ExchangerList lookupAllMailExchanger (const std::string& host) = delete; - - /** - * @brief resolve host mail exchanger. - * @param host host name to resolve. - * @return the first resolved mail exchanger. - */ - static std::string lookupMailExchanger (const std::string& host) = delete; - - private: - /** - * @brief reconnect to the remote DNS server. - * @param endpoint endpoint to connect to. - * @param timeout timeout in milliseconds. - * @return 0 on success, -1 on failure. - */ - virtual int reconnect (const Endpoint& endpoint, std::chrono::milliseconds timeout) override - { - if (this->disconnect () == -1) - { - if (lastError != Errc::TemporaryError) - { - // LCOV_EXCL_START - this->close (); - return -1; - // LCOV_EXCL_STOP - } - - if (!this->waitDisconnected (timeout.count ())) - { - this->close (); - return -1; - } - } - - this->setAlpnProtocols ({"dot"}); - - if (this->connectEncrypted (endpoint) == -1) - { - if (lastError != Errc::TemporaryError) - { - this->close (); - return -1; - } - - if (!this->waitEncrypted (timeout.count ())) - { - this->close (); - return -1; - } - } - - return 0; - } - - /// DOT framing header size. + /// DoT framing header size. static constexpr size_t _frameHeaderSize = 2; /// total expected payload size. diff --git a/fabric/tests/CMakeLists.txt b/fabric/tests/CMakeLists.txt index 272b5235..69ab4950 100644 --- a/fabric/tests/CMakeLists.txt +++ b/fabric/tests/CMakeLists.txt @@ -2,35 +2,65 @@ cmake_minimum_required(VERSION 3.22.1) find_package(GTest REQUIRED) -add_executable(dnsmessage.gtest dnsmessage_test.cpp) -target_link_libraries(dnsmessage.gtest ${JOIN_FABRIC} GTest::gtest_main) -add_test(NAME dnsmessage.gtest COMMAND dnsmessage.gtest) -install(TARGETS dnsmessage.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(dns_message.gtest dns_message_test.cpp) +target_link_libraries(dns_message.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME dns_message.gtest COMMAND dns_message.gtest) +install(TARGETS dns_message.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(dns_protocol.gtest dns_protocol_test.cpp) +target_link_libraries(dns_protocol.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME dns_protocol.gtest COMMAND dns_protocol.gtest) +install(TARGETS dns_protocol.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(dns.gtest dns_test.cpp) target_link_libraries(dns.gtest ${JOIN_FABRIC} GTest::gtest_main) add_test(NAME dns.gtest COMMAND dns.gtest) install(TARGETS dns.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(mdns_protocol.gtest mdns_protocol_test.cpp) +target_link_libraries(mdns_protocol.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME mdns_protocol.gtest COMMAND mdns_protocol.gtest) +install(TARGETS mdns_protocol.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + add_executable(mdns.gtest mdns_test.cpp) target_link_libraries(mdns.gtest ${JOIN_FABRIC} GTest::gtest_main) add_test(NAME mdns.gtest COMMAND mdns.gtest) install(TARGETS mdns.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(dot_protocol.gtest dot_protocol_test.cpp) +target_link_libraries(dot_protocol.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME dot_protocol.gtest COMMAND dot_protocol.gtest) +install(TARGETS dot_protocol.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + add_executable(dot.gtest dot_test.cpp) target_link_libraries(dot.gtest ${JOIN_FABRIC} GTest::gtest_main) add_test(NAME dot.gtest COMMAND dot.gtest) install(TARGETS dot.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(netlink_protocol.gtest netlink_protocol_test.cpp) +target_link_libraries(netlink_protocol.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME netlink_protocol.gtest COMMAND netlink_protocol.gtest) +install(TARGETS netlink_protocol.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(netlink_endpoint.gtest netlink_endpoint_test.cpp) +target_link_libraries(netlink_endpoint.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME netlink_endpoint.gtest COMMAND netlink_endpoint.gtest) +install(TARGETS netlink_endpoint.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(netlink_socket.gtest netlink_socket_test.cpp) +target_link_libraries(netlink_socket.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME netlink_socket.gtest COMMAND netlink_socket.gtest) +install(TARGETS netlink_socket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + add_executable(neighbor.gtest neighbor_test.cpp) target_link_libraries(neighbor.gtest ${JOIN_FABRIC} GTest::gtest_main) add_test(NAME neighbor.gtest COMMAND neighbor.gtest) install(TARGETS neighbor.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(neighbormanager.gtest neighbormanager_test.cpp) -target_link_libraries(neighbormanager.gtest ${JOIN_FABRIC} GTest::gtest_main) -add_test(NAME neighbormanager.gtest COMMAND neighbormanager.gtest) -install(TARGETS neighbormanager.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(neighbor_manager.gtest neighbor_manager_test.cpp) +target_link_libraries(neighbor_manager.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME neighbor_manager.gtest COMMAND neighbor_manager.gtest) +install(TARGETS neighbor_manager.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(arp.gtest arp_test.cpp) target_link_libraries(arp.gtest ${JOIN_FABRIC} GTest::gtest_main) @@ -42,17 +72,17 @@ target_link_libraries(interface.gtest ${JOIN_FABRIC} GTest::gtest_main) add_test(NAME interface.gtest COMMAND interface.gtest) install(TARGETS interface.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(interfacemanager.gtest interfacemanager_test.cpp) -target_link_libraries(interfacemanager.gtest ${JOIN_FABRIC} GTest::gtest_main) -add_test(NAME interfacemanager.gtest COMMAND interfacemanager.gtest) -install(TARGETS interfacemanager.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(interface_manager.gtest interface_manager_test.cpp) +target_link_libraries(interface_manager.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME interface_manager.gtest COMMAND interface_manager.gtest) +install(TARGETS interface_manager.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(route.gtest route_test.cpp) target_link_libraries(route.gtest ${JOIN_FABRIC} GTest::gtest_main) add_test(NAME route.gtest COMMAND route.gtest) install(TARGETS route.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(routemanager.gtest routemanager_test.cpp) -target_link_libraries(routemanager.gtest ${JOIN_FABRIC} GTest::gtest_main) -add_test(NAME routemanager.gtest COMMAND routemanager.gtest) -install(TARGETS routemanager.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(route_manager.gtest route_manager_test.cpp) +target_link_libraries(route_manager.gtest ${JOIN_FABRIC} GTest::gtest_main) +add_test(NAME route_manager.gtest COMMAND route_manager.gtest) +install(TARGETS route_manager.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) diff --git a/fabric/tests/dnsmessage_test.cpp b/fabric/tests/dns_message_test.cpp similarity index 100% rename from fabric/tests/dnsmessage_test.cpp rename to fabric/tests/dns_message_test.cpp diff --git a/fabric/tests/dns_protocol_test.cpp b/fabric/tests/dns_protocol_test.cpp new file mode 100644 index 00000000..b48cb3ab --- /dev/null +++ b/fabric/tests/dns_protocol_test.cpp @@ -0,0 +1,81 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include + +// Libraries. +#include + +using join::Dns; + +/** + * @brief test the family method. + */ +TEST (Dns, family) +{ + ASSERT_EQ (Dns ().family (), AF_INET); + ASSERT_EQ (Dns::v4 ().family (), AF_INET); + ASSERT_EQ (Dns::v6 ().family (), AF_INET6); +} + +/** + * @brief test the type method. + */ +TEST (Dns, type) +{ + ASSERT_EQ (Dns ().type (), SOCK_DGRAM); + ASSERT_EQ (Dns::v4 ().type (), SOCK_DGRAM); + ASSERT_EQ (Dns::v6 ().type (), SOCK_DGRAM); +} + +/** + * @brief test the protocol method. + */ +TEST (Dns, protocol) +{ + ASSERT_EQ (Dns ().protocol (), IPPROTO_UDP); + ASSERT_EQ (Dns::v4 ().protocol (), IPPROTO_UDP); + ASSERT_EQ (Dns::v6 ().protocol (), IPPROTO_UDP); +} + +/** + * @brief test the equal method. + */ +TEST (Dns, equal) +{ + ASSERT_EQ (Dns::v4 (), Dns::v4 ()); + ASSERT_NE (Dns::v4 (), Dns::v6 ()); + ASSERT_EQ (Dns::v6 (), Dns::v6 ()); + ASSERT_NE (Dns::v6 (), Dns::v4 ()); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/fabric/tests/dns_test.cpp b/fabric/tests/dns_test.cpp index 4b0734d6..3884078b 100644 --- a/fabric/tests/dns_test.cpp +++ b/fabric/tests/dns_test.cpp @@ -64,7 +64,6 @@ class DnsTest : public ::testing::Test, public Dns::NameServer */ void TearDown () override { - EXPECT_EQ (_resolver->disconnect (), 0) << lastError.message (); this->close (); } diff --git a/fabric/tests/dot_protocol_test.cpp b/fabric/tests/dot_protocol_test.cpp new file mode 100644 index 00000000..893d11e4 --- /dev/null +++ b/fabric/tests/dot_protocol_test.cpp @@ -0,0 +1,81 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include + +// Libraries. +#include + +using join::Dot; + +/** + * @brief test the family method. + */ +TEST (Dot, family) +{ + ASSERT_EQ (Dot ().family (), AF_INET); + ASSERT_EQ (Dot::v4 ().family (), AF_INET); + ASSERT_EQ (Dot::v6 ().family (), AF_INET6); +} + +/** + * @brief test the type method. + */ +TEST (Dot, type) +{ + ASSERT_EQ (Dot ().type (), SOCK_STREAM); + ASSERT_EQ (Dot::v4 ().type (), SOCK_STREAM); + ASSERT_EQ (Dot::v6 ().type (), SOCK_STREAM); +} + +/** + * @brief test the protocol method. + */ +TEST (Dot, protocol) +{ + ASSERT_EQ (Dot ().protocol (), IPPROTO_TCP); + ASSERT_EQ (Dot::v4 ().protocol (), IPPROTO_TCP); + ASSERT_EQ (Dot::v6 ().protocol (), IPPROTO_TCP); +} + +/** + * @brief test the equal method. + */ +TEST (Dot, equal) +{ + ASSERT_EQ (Dot::v4 (), Dot::v4 ()); + ASSERT_NE (Dot::v4 (), Dot::v6 ()); + ASSERT_EQ (Dot::v6 (), Dot::v6 ()); + ASSERT_NE (Dot::v6 (), Dot::v4 ()); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/fabric/tests/dot_test.cpp b/fabric/tests/dot_test.cpp index a01b2bd4..7694c3f3 100644 --- a/fabric/tests/dot_test.cpp +++ b/fabric/tests/dot_test.cpp @@ -35,6 +35,7 @@ using join::IpAddressList; using join::AliasList; using join::ServerList; using join::ExchangerList; +using join::TlsContext; using namespace std::chrono_literals; @@ -43,21 +44,15 @@ class DotTest : public ::testing::Test protected: void SetUp () override { - _resolver = std::make_unique ("dns.google"); + ASSERT_EQ (_tlsContext.setAlpnProtocols ({"dot"}), 0) << join::lastError.message (); + _resolver = std::make_unique (_tlsContext, "dns.google"); ASSERT_NE (_resolver, nullptr); } - void TearDown () override - { - if (_resolver->disconnect () == -1) - { - ASSERT_EQ (join::lastError, join::Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (_resolver->waitDisconnected ()) << join::lastError.message (); - - _resolver->close (); - } + /// TLS context client. + TlsContext _tlsContext{TlsContext::Role::TlsClient}; + /// DoT resolver. std::unique_ptr _resolver; }; @@ -84,13 +79,13 @@ TEST_F (DotTest, resolveAllAddress) addresses = _resolver->resolveAllAddress ("localhost"); EXPECT_EQ (addresses.size (), 0); - addresses = Dot::Resolver ("255.255.255.255").resolveAllAddress ("google.com", AF_INET); + addresses = Dot::Resolver (_tlsContext, "255.255.255.255").resolveAllAddress ("google.com", AF_INET); EXPECT_EQ (addresses.size (), 0); - addresses = Dot::Resolver ("8.8.8.8", 853).resolveAllAddress ("joinframework.net", AF_INET, 1ms); + addresses = Dot::Resolver (_tlsContext, "8.8.8.8", 853).resolveAllAddress ("joinframework.net", AF_INET, 1ms); EXPECT_EQ (addresses.size (), 0); - addresses = Dot::Resolver ("8.8.8.8", 853).resolveAllAddress ("joinframework.net", 1ms); + addresses = Dot::Resolver (_tlsContext, "8.8.8.8", 853).resolveAllAddress ("joinframework.net", 1ms); EXPECT_EQ (addresses.size (), 0); addresses = _resolver->resolveAllAddress ("joinframework.net", AF_INET); @@ -129,13 +124,13 @@ TEST_F (DotTest, resolveAddress) address = _resolver->resolveAddress ("localhost"); EXPECT_TRUE (address.isWildcard ()); - address = Dot::Resolver ("255.255.255.255").resolveAddress ("google.com", AF_INET); + address = Dot::Resolver (_tlsContext, "255.255.255.255").resolveAddress ("google.com", AF_INET); EXPECT_TRUE (address.isWildcard ()); - address = Dot::Resolver ("8.8.8.8", 853).resolveAddress ("joinframework.net", AF_INET, 1ms); + address = Dot::Resolver (_tlsContext, "8.8.8.8", 853).resolveAddress ("joinframework.net", AF_INET, 1ms); EXPECT_TRUE (address.isWildcard ()); - address = Dot::Resolver ("8.8.8.8", 853).resolveAddress ("joinframework.net", 1ms); + address = Dot::Resolver (_tlsContext, "8.8.8.8", 853).resolveAddress ("joinframework.net", 1ms); EXPECT_TRUE (address.isWildcard ()); address = _resolver->resolveAddress ("joinframework.net", AF_INET); @@ -167,11 +162,11 @@ TEST_F (DotTest, resolveAllName) aliases = _resolver->resolveAllName ("192.168.24.32"); EXPECT_EQ (aliases.size (), 0); - aliases = Dot::Resolver ("255.255.255.255").resolveAllName ("1.1.1.1"); + aliases = Dot::Resolver (_tlsContext, "255.255.255.255").resolveAllName ("1.1.1.1"); EXPECT_EQ (aliases.size (), 0); - aliases = - Dot::Resolver ("8.8.8.8", 853).resolveAllName (_resolver->resolveAddress ("joinframework.net", AF_INET), 1ms); + aliases = Dot::Resolver (_tlsContext, "8.8.8.8", 853) + .resolveAllName (_resolver->resolveAddress ("joinframework.net", AF_INET), 1ms); EXPECT_EQ (aliases.size (), 0); aliases = _resolver->resolveAllName ("1.1.1.1"); @@ -192,10 +187,11 @@ TEST_F (DotTest, resolveName) alias = _resolver->resolveName ("192.168.24.32"); EXPECT_TRUE (alias.empty ()); - alias = Dot::Resolver ("255.255.255.255").resolveName ("1.1.1.1"); + alias = Dot::Resolver (_tlsContext, "255.255.255.255").resolveName ("1.1.1.1"); EXPECT_TRUE (alias.empty ()); - alias = Dot::Resolver ("8.8.8.8", 853).resolveName (_resolver->resolveAddress ("joinframework.net", AF_INET), 1ms); + alias = Dot::Resolver (_tlsContext, "8.8.8.8", 853) + .resolveName (_resolver->resolveAddress ("joinframework.net", AF_INET), 1ms); EXPECT_TRUE (alias.empty ()); alias = _resolver->resolveName ("1.1.1.1"); @@ -213,10 +209,10 @@ TEST_F (DotTest, resolveAllNameServer) servers = _resolver->resolveAllNameServer ("localhost"); EXPECT_EQ (servers.size (), 0); - servers = Dot::Resolver ("255.255.255.255").resolveAllNameServer ("google.com"); + servers = Dot::Resolver (_tlsContext, "255.255.255.255").resolveAllNameServer ("google.com"); EXPECT_EQ (servers.size (), 0); - servers = Dot::Resolver ("8.8.8.8", 853).resolveAllNameServer ("joinframework.net", 1ms); + servers = Dot::Resolver (_tlsContext, "8.8.8.8", 853).resolveAllNameServer ("joinframework.net", 1ms); EXPECT_EQ (servers.size (), 0); servers = _resolver->resolveAllNameServer ("google.com"); @@ -237,10 +233,10 @@ TEST_F (DotTest, resolveNameServer) server = _resolver->resolveNameServer ("localhost"); EXPECT_TRUE (server.empty ()); - server = Dot::Resolver ("255.255.255.255").resolveNameServer ("google.com"); + server = Dot::Resolver (_tlsContext, "255.255.255.255").resolveNameServer ("google.com"); EXPECT_TRUE (server.empty ()); - server = Dot::Resolver ("8.8.8.8", 853).resolveNameServer ("joinframework.net", 1ms); + server = Dot::Resolver (_tlsContext, "8.8.8.8", 853).resolveNameServer ("joinframework.net", 1ms); EXPECT_TRUE (server.empty ()); server = _resolver->resolveNameServer ("google.com"); @@ -261,10 +257,10 @@ TEST_F (DotTest, resolveAuthority) authority = _resolver->resolveAuthority ("localhost"); EXPECT_TRUE (authority.empty ()); - authority = Dot::Resolver ("255.255.255.255").resolveAuthority ("google.com"); + authority = Dot::Resolver (_tlsContext, "255.255.255.255").resolveAuthority ("google.com"); EXPECT_TRUE (authority.empty ()); - authority = Dot::Resolver ("8.8.8.8", 853).resolveAuthority ("joinframework.net", 1ms); + authority = Dot::Resolver (_tlsContext, "8.8.8.8", 853).resolveAuthority ("joinframework.net", 1ms); EXPECT_TRUE (authority.empty ()); authority = _resolver->resolveAuthority ("google.com"); @@ -285,10 +281,10 @@ TEST_F (DotTest, resolveAllMailExchanger) exchangers = _resolver->resolveAllMailExchanger ("localhost"); EXPECT_EQ (exchangers.size (), 0); - exchangers = Dot::Resolver ("255.255.255.255").resolveAllMailExchanger ("google.com"); + exchangers = Dot::Resolver (_tlsContext, "255.255.255.255").resolveAllMailExchanger ("google.com"); EXPECT_EQ (exchangers.size (), 0); - exchangers = Dot::Resolver ("8.8.8.8", 853).resolveAllMailExchanger ("joinframework.net", 1ms); + exchangers = Dot::Resolver (_tlsContext, "8.8.8.8", 853).resolveAllMailExchanger ("joinframework.net", 1ms); EXPECT_EQ (exchangers.size (), 0); exchangers = _resolver->resolveAllMailExchanger ("google.com"); @@ -306,10 +302,10 @@ TEST_F (DotTest, resolveMailExchanger) exchanger = _resolver->resolveMailExchanger ("localhost"); EXPECT_TRUE (exchanger.empty ()); - exchanger = Dot::Resolver ("255.255.255.255").resolveMailExchanger ("google.com"); + exchanger = Dot::Resolver (_tlsContext, "255.255.255.255").resolveMailExchanger ("google.com"); EXPECT_TRUE (exchanger.empty ()); - exchanger = Dot::Resolver ("8.8.8.8", 853).resolveMailExchanger ("joinframework.net", 1ms); + exchanger = Dot::Resolver (_tlsContext, "8.8.8.8", 853).resolveMailExchanger ("joinframework.net", 1ms); EXPECT_TRUE (exchanger.empty ()); exchanger = _resolver->resolveMailExchanger ("google.com"); diff --git a/fabric/tests/interfacemanager_test.cpp b/fabric/tests/interface_manager_test.cpp similarity index 100% rename from fabric/tests/interfacemanager_test.cpp rename to fabric/tests/interface_manager_test.cpp diff --git a/fabric/tests/mdns_protocol_test.cpp b/fabric/tests/mdns_protocol_test.cpp new file mode 100644 index 00000000..e9eb7137 --- /dev/null +++ b/fabric/tests/mdns_protocol_test.cpp @@ -0,0 +1,90 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include + +// Libraries. +#include + +using join::Mdns; + +/** + * @brief test the family method. + */ +TEST (Mdns, family) +{ + ASSERT_EQ (Mdns ().family (), AF_INET); + ASSERT_EQ (Mdns::v4 ().family (), AF_INET); + ASSERT_EQ (Mdns::v6 ().family (), AF_INET6); +} + +/** + * @brief test the type method. + */ +TEST (Mdns, type) +{ + ASSERT_EQ (Mdns ().type (), SOCK_DGRAM); + ASSERT_EQ (Mdns::v4 ().type (), SOCK_DGRAM); + ASSERT_EQ (Mdns::v6 ().type (), SOCK_DGRAM); +} + +/** + * @brief test the protocol method. + */ +TEST (Mdns, protocol) +{ + ASSERT_EQ (Mdns ().protocol (), IPPROTO_UDP); + ASSERT_EQ (Mdns::v4 ().protocol (), IPPROTO_UDP); + ASSERT_EQ (Mdns::v6 ().protocol (), IPPROTO_UDP); +} + +/** + * @brief test the equal method. + */ +TEST (Mdns, equal) +{ + ASSERT_EQ (Mdns::v4 (), Mdns::v4 ()); + ASSERT_NE (Mdns::v4 (), Mdns::v6 ()); + ASSERT_EQ (Mdns::v6 (), Mdns::v6 ()); + ASSERT_NE (Mdns::v6 (), Mdns::v4 ()); +} + +/** + * @brief test the multicast address method. + */ +TEST (Mdns, multicastAddress) +{ + ASSERT_EQ (Mdns::multicastAddress (AF_INET), "224.0.0.251"); + ASSERT_EQ (Mdns::multicastAddress (AF_INET6), "ff02::fb"); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/fabric/tests/neighbormanager_test.cpp b/fabric/tests/neighbor_manager_test.cpp similarity index 100% rename from fabric/tests/neighbormanager_test.cpp rename to fabric/tests/neighbor_manager_test.cpp diff --git a/fabric/tests/netlink_endpoint_test.cpp b/fabric/tests/netlink_endpoint_test.cpp new file mode 100644 index 00000000..f097b1d2 --- /dev/null +++ b/fabric/tests/netlink_endpoint_test.cpp @@ -0,0 +1,104 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include + +// Libraries. +#include + +using join::Netlink; + +/** + * @brief test the addr method. + */ +TEST (NetlinkEndpoint, addr) +{ + Netlink::Endpoint netlinkEndpoint; + ASSERT_NE (netlinkEndpoint.addr (), nullptr); +} + +/** + * @brief test the length method. + */ +TEST (NetlinkEndpoint, length) +{ + Netlink::Endpoint netlinkEndpoint; + ASSERT_EQ (netlinkEndpoint.length (), sizeof (struct sockaddr_nl)); +} + +/** + * @brief test the device method. + */ +TEST (NetlinkEndpoint, device) +{ + Netlink::Endpoint netlinkEndpoint; + ASSERT_EQ (netlinkEndpoint.device (), ""); +} + +/** + * @brief test the protocol method. + */ +TEST (NetlinkEndpoint, protocol) +{ + ASSERT_EQ (Netlink::Endpoint ().protocol (), Netlink::rt ()); + ASSERT_EQ (Netlink::Endpoint (Netlink::rt (), RTMGRP_LINK).protocol (), Netlink::rt ()); + ASSERT_NE (Netlink::Endpoint (Netlink::rt (), RTMGRP_LINK).protocol (), Netlink::nf ()); + ASSERT_EQ (Netlink::Endpoint (Netlink::nf (), NFNLGRP_NONE).protocol (), Netlink::nf ()); + ASSERT_EQ (Netlink::Endpoint (RTMGRP_LINK).protocol (), Netlink::rt ()); + ASSERT_NE (Netlink::Endpoint (RTMGRP_LINK).protocol (), Netlink::nf ()); +} + +/** + * @brief test the equal method. + */ +TEST (NetlinkEndpoint, equal) +{ + ASSERT_EQ (Netlink::Endpoint (RTMGRP_LINK), Netlink::Endpoint (RTMGRP_LINK)); + ASSERT_NE (Netlink::Endpoint (RTMGRP_LINK), Netlink::Endpoint (RTMGRP_IPV4_IFADDR)); + ASSERT_EQ (Netlink::Endpoint (RTMGRP_IPV4_IFADDR), Netlink::Endpoint (RTMGRP_IPV4_IFADDR)); + ASSERT_NE (Netlink::Endpoint (RTMGRP_IPV4_IFADDR), Netlink::Endpoint (RTMGRP_LINK)); +} + +/** + * @brief test the serialize method. + */ +TEST (NetlinkEndpoint, serialize) +{ + std::stringstream stream; + Netlink::Endpoint netlinkEndpoint (RTMGRP_LINK); + ASSERT_NO_THROW (stream << netlinkEndpoint); + std::stringstream ss; + ss << "pid=" << getpid () << ",groups=" << uint32_t (RTMGRP_LINK); + ASSERT_EQ (stream.str (), ss.str ()); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/fabric/tests/netlink_protocol_test.cpp b/fabric/tests/netlink_protocol_test.cpp new file mode 100644 index 00000000..22981513 --- /dev/null +++ b/fabric/tests/netlink_protocol_test.cpp @@ -0,0 +1,81 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include + +// Libraries. +#include + +using join::Netlink; + +/** + * @brief test the family method. + */ +TEST (Netlink, family) +{ + ASSERT_EQ (Netlink ().family (), AF_NETLINK); + ASSERT_EQ (Netlink::rt ().family (), AF_NETLINK); + ASSERT_EQ (Netlink::nf ().family (), AF_NETLINK); +} + +/** + * @brief test the type method. + */ +TEST (Netlink, type) +{ + ASSERT_EQ (Netlink ().type (), SOCK_RAW); + ASSERT_EQ (Netlink::rt ().type (), SOCK_RAW); + ASSERT_EQ (Netlink::nf ().type (), SOCK_RAW); +} + +/** + * @brief test the protocol method. + */ +TEST (Netlink, protocol) +{ + ASSERT_EQ (Netlink ().protocol (), NETLINK_ROUTE); + ASSERT_EQ (Netlink::rt ().protocol (), NETLINK_ROUTE); + ASSERT_EQ (Netlink::nf ().protocol (), NETLINK_NETFILTER); +} + +/** + * @brief test the equal method. + */ +TEST (Netlink, equal) +{ + ASSERT_EQ (Netlink::rt (), Netlink::rt ()); + ASSERT_NE (Netlink::rt (), Netlink::nf ()); + ASSERT_EQ (Netlink::nf (), Netlink::nf ()); + ASSERT_NE (Netlink::nf (), Netlink::rt ()); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/core/tests/netlinksocket_test.cpp b/fabric/tests/netlink_socket_test.cpp similarity index 99% rename from core/tests/netlinksocket_test.cpp rename to fabric/tests/netlink_socket_test.cpp index c0c3431e..dbd20ff7 100644 --- a/core/tests/netlinksocket_test.cpp +++ b/fabric/tests/netlink_socket_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/fabric/tests/routemanager_test.cpp b/fabric/tests/route_manager_test.cpp similarity index 100% rename from fabric/tests/routemanager_test.cpp rename to fabric/tests/route_manager_test.cpp From f6050c9bfe08cff1e27c1a3610de32fbb605a64d Mon Sep 17 00:00:00 2001 From: mrabine Date: Fri, 12 Jun 2026 22:05:55 +0200 Subject: [PATCH 5/9] renaming --- core/CMakeLists.txt | 16 +- core/include/join/acceptor.hpp | 2 +- core/include/join/datagram_socket.hpp | 386 ++++++ core/include/join/endpoint.hpp | 2 +- .../join/{ipaddress.hpp => ip_address.hpp} | 4 +- .../join/{macaddress.hpp => mac_address.hpp} | 6 +- core/include/join/protocol.hpp | 7 +- core/include/join/socket.hpp | 1089 +++-------------- .../{socketstream.hpp => socket_stream.hpp} | 141 ++- core/include/join/stream_socket.hpp | 493 ++++++++ .../join/{threadpool.hpp => thread_pool.hpp} | 4 +- core/src/{ipaddress.cpp => ip_address.cpp} | 14 +- core/src/{macaddress.cpp => mac_address.cpp} | 2 +- core/src/{threadpool.cpp => thread_pool.cpp} | 2 +- core/tests/CMakeLists.txt | 300 ++--- ...mpsocket_test.cpp => icmp_socket_test.cpp} | 16 +- ...ipaddress_test.cpp => ip_address_test.cpp} | 2 +- ...calalloc_test.cpp => local_alloc_test.cpp} | 0 .../{localmem_test.cpp => local_mem_test.cpp} | 0 ...localmpmc_test.cpp => local_mpmc_test.cpp} | 0 ...localmpsc_test.cpp => local_mpsc_test.cpp} | 0 ...localspsc_test.cpp => local_spsc_test.cpp} | 0 ...caddress_test.cpp => mac_address_test.cpp} | 2 +- ...tats_test.cpp => monotonic_stats_test.cpp} | 0 ...imer_test.cpp => monotonic_timer_test.cpp} | 0 ...s_test.cpp => monotonicraw_stats_test.cpp} | 0 ...rawsocket_test.cpp => raw_socket_test.cpp} | 16 +- ...tscstats_test.cpp => rdtsc_stats_test.cpp} | 0 core/tests/reactor_test.cpp | 3 +- ...timer_test.cpp => realtime_timer_test.cpp} | 0 ...utex_test.cpp => recursive_mutex_test.cpp} | 0 ...ion_test.cpp => shared_condition_test.cpp} | 0 ...edfutex_test.cpp => shared_futex_test.cpp} | 0 ...edmutex_test.cpp => shared_mutex_test.cpp} | 0 ...ore_test.cpp => shared_semaphore_test.cpp} | 0 .../{shmalloc_test.cpp => shm_alloc_test.cpp} | 0 .../{shmmem_test.cpp => shm_mem_test.cpp} | 0 .../{shmmpmc_test.cpp => shm_mpmc_test.cpp} | 0 .../{shmmpsc_test.cpp => shm_mpsc_test.cpp} | 0 .../{shmspsc_test.cpp => shm_spsc_test.cpp} | 0 ...cceptor_test.cpp => tcp_acceptor_test.cpp} | 0 ...am_test.cpp => tcp_socket_stream_test.cpp} | 35 +- ...tcpsocket_test.cpp => tcp_socket_test.cpp} | 45 +- ...readpool_test.cpp => thread_pool_test.cpp} | 2 +- ...udpsocket_test.cpp => udp_socket_test.cpp} | 37 +- ...test.cpp => unix_datagram_socket_test.cpp} | 37 +- ...test.cpp => unix_stream_acceptor_test.cpp} | 0 ...t_test.cpp => unix_stream_socket_test.cpp} | 33 +- crypto/CMakeLists.txt | 18 +- crypto/include/join/dtls_wrapper.hpp | 156 +++ crypto/include/join/openssl.hpp | 1 + .../include/join/{tlswrapper.hpp => tls.hpp} | 288 ++--- .../join/{tlscontext.hpp => tls_context.hpp} | 6 +- .../join/{tlserror.hpp => tls_error.hpp} | 4 +- .../include/join/{tlskey.hpp => tls_key.hpp} | 4 +- crypto/include/join/tls_protocol.hpp | 230 ++++ crypto/include/join/tls_stream.hpp | 175 +++ crypto/include/join/tls_wrapper.hpp | 104 ++ crypto/src/openssl.cpp | 1 - crypto/src/signature.cpp | 2 +- .../src/{tlscontext.cpp => tls_context.cpp} | 2 +- crypto/src/{tlserror.cpp => tls_error.cpp} | 2 +- crypto/src/{tlskey.cpp => tls_key.cpp} | 2 +- crypto/tests/CMakeLists.txt | 64 +- ...sterror_test.cpp => digest_error_test.cpp} | 0 ...wrapper_test.cpp => dtls_wrapper_test.cpp} | 171 +-- ...scontext_test.cpp => tls_context_test.cpp} | 2 +- .../{tlserror_test.cpp => tls_error_test.cpp} | 2 +- .../{tlskey_test.cpp => tls_key_test.cpp} | 2 +- crypto/tests/tls_protocol_test.cpp | 88 ++ crypto/tests/tls_stream_test.cpp | 837 +++++++++++++ ...swrapper_test.cpp => tls_wrapper_test.cpp} | 108 +- data/tests/CMakeLists.txt | 86 +- ...r_test.cpp => json_canonicalizer_test.cpp} | 0 ...jsonerror_test.cpp => json_error_test.cpp} | 0 ...onreader_test.cpp => json_reader_test.cpp} | 0 ...onwriter_test.cpp => json_writer_test.cpp} | 0 ...ckreader_test.cpp => pack_reader_test.cpp} | 0 ...ckwriter_test.cpp => pack_writer_test.cpp} | 0 .../{saxerror_test.cpp => sax_error_test.cpp} | 0 ...reamview_test.cpp => stream_view_test.cpp} | 0 ...ringview_test.cpp => string_view_test.cpp} | 0 fabric/CMakeLists.txt | 22 +- fabric/include/join/arp.hpp | 4 +- .../join/{dnsmessage.hpp => dns_message.hpp} | 6 +- .../join/{dns.hpp => dns_protocol.hpp} | 8 +- .../join/{dot.hpp => dot_protocol.hpp} | 13 +- fabric/include/join/interface.hpp | 4 +- ...rfacemanager.hpp => interface_manager.hpp} | 10 +- .../join/{mdns.hpp => mdns_protocol.hpp} | 8 +- fabric/include/join/nameserver.hpp | 7 +- fabric/include/join/neighbor.hpp | 4 +- ...ighbormanager.hpp => neighbor_manager.hpp} | 6 +- ...netlinkmanager.hpp => netlink_manager.hpp} | 12 +- .../{netlink.hpp => netlink_protocol.hpp} | 6 +- fabric/include/join/resolver.hpp | 38 +- fabric/include/join/route.hpp | 2 +- .../{routemanager.hpp => route_manager.hpp} | 6 +- fabric/src/interface.cpp | 2 +- ...rfacemanager.cpp => interface_manager.cpp} | 2 +- fabric/src/neighbor.cpp | 2 +- ...ighbormanager.cpp => neighbor_manager.cpp} | 2 +- ...netlinkmanager.cpp => netlink_manager.cpp} | 16 +- fabric/src/route.cpp | 2 +- .../{routemanager.cpp => route_manager.cpp} | 2 +- fabric/tests/dns_message_test.cpp | 2 +- fabric/tests/dns_protocol_test.cpp | 2 +- fabric/tests/dot_protocol_test.cpp | 2 +- fabric/tests/interface_manager_test.cpp | 2 +- fabric/tests/interface_test.cpp | 2 +- fabric/tests/mdns_protocol_test.cpp | 2 +- fabric/tests/neighbor_manager_test.cpp | 2 +- fabric/tests/neighbor_test.cpp | 2 +- fabric/tests/netlink_endpoint_test.cpp | 2 +- fabric/tests/netlink_protocol_test.cpp | 2 +- fabric/tests/netlink_socket_test.cpp | 20 +- fabric/tests/route_manager_test.cpp | 2 +- fabric/tests/route_test.cpp | 2 +- services/CMakeLists.txt | 18 +- .../{chunkstream.hpp => chunk_stream.hpp} | 4 +- .../{httpmessage.hpp => http_message.hpp} | 4 +- services/include/join/httpclient.hpp | 2 +- .../{mailmessage.hpp => mail_message.hpp} | 4 +- services/include/join/smtpclient.hpp | 2 +- services/src/cache.cpp | 4 +- .../src/{chunkstream.cpp => chunk_stream.cpp} | 2 +- .../src/{httpmessage.cpp => http_message.cpp} | 2 +- .../src/{mailmessage.cpp => mail_message.cpp} | 2 +- services/tests/CMakeLists.txt | 108 +- ...kstream_test.cpp => chunk_stream_test.cpp} | 2 +- ...httperror_test.cpp => http_error_test.cpp} | 2 +- ...request_test.cpp => http_request_test.cpp} | 2 +- ...sponse_test.cpp => http_response_test.cpp} | 2 +- ...message_test.cpp => mail_message_test.cpp} | 2 +- ...pient_test.cpp => mail_recipient_test.cpp} | 2 +- ...ilsender_test.cpp => mail_sender_test.cpp} | 2 +- 136 files changed, 3432 insertions(+), 2013 deletions(-) create mode 100644 core/include/join/datagram_socket.hpp rename core/include/join/{ipaddress.hpp => ip_address.hpp} (99%) rename core/include/join/{macaddress.hpp => mac_address.hpp} (99%) rename core/include/join/{socketstream.hpp => socket_stream.hpp} (77%) create mode 100644 core/include/join/stream_socket.hpp rename core/include/join/{threadpool.hpp => thread_pool.hpp} (99%) rename core/src/{ipaddress.cpp => ip_address.cpp} (99%) rename core/src/{macaddress.cpp => mac_address.cpp} (99%) rename core/src/{threadpool.cpp => thread_pool.cpp} (99%) rename core/tests/{icmpsocket_test.cpp => icmp_socket_test.cpp} (98%) rename core/tests/{ipaddress_test.cpp => ip_address_test.cpp} (99%) rename core/tests/{localalloc_test.cpp => local_alloc_test.cpp} (100%) rename core/tests/{localmem_test.cpp => local_mem_test.cpp} (100%) rename core/tests/{localmpmc_test.cpp => local_mpmc_test.cpp} (100%) rename core/tests/{localmpsc_test.cpp => local_mpsc_test.cpp} (100%) rename core/tests/{localspsc_test.cpp => local_spsc_test.cpp} (100%) rename core/tests/{macaddress_test.cpp => mac_address_test.cpp} (99%) rename core/tests/{monotonicstats_test.cpp => monotonic_stats_test.cpp} (100%) rename core/tests/{monotonictimer_test.cpp => monotonic_timer_test.cpp} (100%) rename core/tests/{monotonicrawstats_test.cpp => monotonicraw_stats_test.cpp} (100%) rename core/tests/{rawsocket_test.cpp => raw_socket_test.cpp} (97%) rename core/tests/{rdtscstats_test.cpp => rdtsc_stats_test.cpp} (100%) rename core/tests/{realtimetimer_test.cpp => realtime_timer_test.cpp} (100%) rename core/tests/{recursivemutex_test.cpp => recursive_mutex_test.cpp} (100%) rename core/tests/{sharedcondition_test.cpp => shared_condition_test.cpp} (100%) rename core/tests/{sharedfutex_test.cpp => shared_futex_test.cpp} (100%) rename core/tests/{sharedmutex_test.cpp => shared_mutex_test.cpp} (100%) rename core/tests/{sharedsemaphore_test.cpp => shared_semaphore_test.cpp} (100%) rename core/tests/{shmalloc_test.cpp => shm_alloc_test.cpp} (100%) rename core/tests/{shmmem_test.cpp => shm_mem_test.cpp} (100%) rename core/tests/{shmmpmc_test.cpp => shm_mpmc_test.cpp} (100%) rename core/tests/{shmmpsc_test.cpp => shm_mpsc_test.cpp} (100%) rename core/tests/{shmspsc_test.cpp => shm_spsc_test.cpp} (100%) rename core/tests/{tcpacceptor_test.cpp => tcp_acceptor_test.cpp} (100%) rename core/tests/{tcpsocketstream_test.cpp => tcp_socket_stream_test.cpp} (93%) rename core/tests/{tcpsocket_test.cpp => tcp_socket_test.cpp} (93%) rename core/tests/{threadpool_test.cpp => thread_pool_test.cpp} (98%) rename core/tests/{udpsocket_test.cpp => udp_socket_test.cpp} (94%) rename core/tests/{unixdgramsocket_test.cpp => unix_datagram_socket_test.cpp} (93%) rename core/tests/{unixstreamacceptor_test.cpp => unix_stream_acceptor_test.cpp} (100%) rename core/tests/{unixstreamsocket_test.cpp => unix_stream_socket_test.cpp} (95%) create mode 100644 crypto/include/join/dtls_wrapper.hpp rename crypto/include/join/{tlswrapper.hpp => tls.hpp} (79%) rename crypto/include/join/{tlscontext.hpp => tls_context.hpp} (98%) rename crypto/include/join/{tlserror.hpp => tls_error.hpp} (97%) rename crypto/include/join/{tlskey.hpp => tls_key.hpp} (98%) create mode 100644 crypto/include/join/tls_protocol.hpp create mode 100644 crypto/include/join/tls_stream.hpp create mode 100644 crypto/include/join/tls_wrapper.hpp rename crypto/src/{tlscontext.cpp => tls_context.cpp} (99%) rename crypto/src/{tlserror.cpp => tls_error.cpp} (99%) rename crypto/src/{tlskey.cpp => tls_key.cpp} (99%) rename crypto/tests/{digesterror_test.cpp => digest_error_test.cpp} (100%) rename crypto/tests/{dtlswrapper_test.cpp => dtls_wrapper_test.cpp} (86%) rename crypto/tests/{tlscontext_test.cpp => tls_context_test.cpp} (99%) rename crypto/tests/{tlserror_test.cpp => tls_error_test.cpp} (99%) rename crypto/tests/{tlskey_test.cpp => tls_key_test.cpp} (99%) create mode 100644 crypto/tests/tls_protocol_test.cpp create mode 100644 crypto/tests/tls_stream_test.cpp rename crypto/tests/{tlswrapper_test.cpp => tls_wrapper_test.cpp} (92%) rename data/tests/{jsoncanonicalizer_test.cpp => json_canonicalizer_test.cpp} (100%) rename data/tests/{jsonerror_test.cpp => json_error_test.cpp} (100%) rename data/tests/{jsonreader_test.cpp => json_reader_test.cpp} (100%) rename data/tests/{jsonwriter_test.cpp => json_writer_test.cpp} (100%) rename data/tests/{packreader_test.cpp => pack_reader_test.cpp} (100%) rename data/tests/{packwriter_test.cpp => pack_writer_test.cpp} (100%) rename data/tests/{saxerror_test.cpp => sax_error_test.cpp} (100%) rename data/tests/{streamview_test.cpp => stream_view_test.cpp} (100%) rename data/tests/{stringview_test.cpp => string_view_test.cpp} (100%) rename fabric/include/join/{dnsmessage.hpp => dns_message.hpp} (99%) rename fabric/include/join/{dns.hpp => dns_protocol.hpp} (95%) rename fabric/include/join/{dot.hpp => dot_protocol.hpp} (92%) rename fabric/include/join/{interfacemanager.hpp => interface_manager.hpp} (99%) rename fabric/include/join/{mdns.hpp => mdns_protocol.hpp} (95%) rename fabric/include/join/{neighbormanager.hpp => neighbor_manager.hpp} (99%) rename fabric/include/join/{netlinkmanager.hpp => netlink_manager.hpp} (96%) rename fabric/include/join/{netlink.hpp => netlink_protocol.hpp} (96%) rename fabric/include/join/{routemanager.hpp => route_manager.hpp} (99%) rename fabric/src/{interfacemanager.cpp => interface_manager.cpp} (99%) rename fabric/src/{neighbormanager.cpp => neighbor_manager.cpp} (99%) rename fabric/src/{netlinkmanager.cpp => netlink_manager.cpp} (94%) rename fabric/src/{routemanager.cpp => route_manager.cpp} (99%) rename services/include/join/{chunkstream.hpp => chunk_stream.hpp} (98%) rename services/include/join/{httpmessage.hpp => http_message.hpp} (99%) rename services/include/join/{mailmessage.hpp => mail_message.hpp} (99%) rename services/src/{chunkstream.cpp => chunk_stream.cpp} (99%) rename services/src/{httpmessage.cpp => http_message.cpp} (99%) rename services/src/{mailmessage.cpp => mail_message.cpp} (99%) rename services/tests/{chunkstream_test.cpp => chunk_stream_test.cpp} (99%) rename services/tests/{httperror_test.cpp => http_error_test.cpp} (99%) rename services/tests/{httprequest_test.cpp => http_request_test.cpp} (99%) rename services/tests/{httpresponse_test.cpp => http_response_test.cpp} (99%) rename services/tests/{mailmessage_test.cpp => mail_message_test.cpp} (99%) rename services/tests/{mailrecipient_test.cpp => mail_recipient_test.cpp} (99%) rename services/tests/{mailsender_test.cpp => mail_sender_test.cpp} (98%) diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 23ce6486..a9e22be1 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -28,13 +28,15 @@ set(PUBLIC_HEADERS include/join/futex.hpp include/join/function.hpp include/join/thread.hpp - include/join/threadpool.hpp - include/join/macaddress.hpp - include/join/ipaddress.hpp + include/join/thread_pool.hpp + include/join/mac_address.hpp + include/join/ip_address.hpp include/join/endpoint.hpp include/join/protocol.hpp include/join/socket.hpp - include/join/socketstream.hpp + include/join/datagram_socket.hpp + include/join/stream_socket.hpp + include/join/socket_stream.hpp include/join/acceptor.hpp include/join/cpu.hpp include/join/clock.hpp @@ -56,9 +58,9 @@ set(SOURCES src/condition.cpp src/semaphore.cpp src/thread.cpp - src/threadpool.cpp - src/macaddress.cpp - src/ipaddress.cpp + src/thread_pool.cpp + src/mac_address.cpp + src/ip_address.cpp src/cpu.cpp ) diff --git a/core/include/join/acceptor.hpp b/core/include/join/acceptor.hpp index 5f538f8b..9ddc1e2f 100644 --- a/core/include/join/acceptor.hpp +++ b/core/include/join/acceptor.hpp @@ -26,7 +26,7 @@ #define JOIN_CORE_ACCEPTOR_HPP // libjoin. -#include +#include namespace join { diff --git a/core/include/join/datagram_socket.hpp b/core/include/join/datagram_socket.hpp new file mode 100644 index 00000000..d441981f --- /dev/null +++ b/core/include/join/datagram_socket.hpp @@ -0,0 +1,386 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_CORE_DATAGRAM_SOCKET_HPP +#define JOIN_CORE_DATAGRAM_SOCKET_HPP + +// libjoin. +#include + +// C. +#include + +namespace join +{ + /** + * @brief basic datagram socket class. + */ + template + class BasicDatagramSocket final : public BasicSocket + { + public: + using Ptr = std::unique_ptr>; + using Mode = typename BasicSocket::Mode; + using Option = typename BasicSocket::Option; + using State = typename BasicSocket::State; + using Endpoint = typename Protocol::Endpoint; + + /** + * @brief Default constructor. + */ + BasicDatagramSocket () + : BasicDatagramSocket (Mode::NonBlocking) + { + } + + /** + * @brief Create instance specifying the time to live. + * @param ttl packet time to live. + */ + explicit BasicDatagramSocket (int ttl) + : BasicDatagramSocket (Mode::NonBlocking, ttl) + { + } + + /** + * @brief Create instance specifying the mode. + * @param mode Set the socket blocking mode. + * @param ttl packet time to live. + */ + explicit BasicDatagramSocket (Mode mode, int ttl = 60) + : BasicSocket (mode) + , _ttl (ttl) + { + } + + /** + * @brief Copy constructor. + * @param other Other object to copy. + */ + BasicDatagramSocket (const BasicDatagramSocket& other) = delete; + + /** + * @brief Copy assignment operator. + * @param other other object to assign. + * @return assigned object. + */ + BasicDatagramSocket& operator= (const BasicDatagramSocket& other) = delete; + + /** + * @brief Move constructor. + * @param other Other object to move. + */ + BasicDatagramSocket (BasicDatagramSocket&& other) noexcept + : BasicSocket (std::move (other)) + , _remote (std::move (other._remote)) + , _ttl (other._ttl) + { + other._ttl = 60; + } + + /** + * @brief Move assignment operator. + * @param other other object to assign. + * @return assigned object. + */ + BasicDatagramSocket& operator= (BasicDatagramSocket&& other) noexcept + { + BasicSocket::operator= (std::move (other)); + + _remote = std::move (other._remote); + _ttl = other._ttl; + + other._ttl = 60; + + return *this; + } + + /** + * @brief Destroy the instance. + */ + ~BasicDatagramSocket () = default; + + /** + * @brief open socket using the given protocol. + * @param protocol protocol to use. + * @return 0 on success, -1 on failure. + */ + int open (const Protocol& protocol = Protocol ()) noexcept override + { + int result = BasicSocket::open (protocol); + if (result == -1) + { + return -1; + } + + int off = 0; + + if ((protocol.protocol () == IPPROTO_UDP) || (protocol.protocol () == IPPROTO_TCP)) + { + if ((protocol.family () == AF_INET6) && + (::setsockopt (this->_handle, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof (off)) == -1)) + { + lastError = std::error_code (errno, std::generic_category ()); + close (); + return -1; + } + } + + if ((protocol.protocol () == IPPROTO_ICMPV6) || (protocol.protocol () == IPPROTO_ICMP)) + { + if ((protocol.family () == AF_INET) && + (::setsockopt (this->_handle, IPPROTO_IP, IP_HDRINCL, &off, sizeof (off)) == -1)) + { + lastError = std::error_code (errno, std::generic_category ()); + close (); + return -1; + } + + this->setOption (Option::MulticastTtl, _ttl); + this->setOption (Option::Ttl, _ttl); + } + + return 0; + } + + /** + * @brief assign the default remote endpoint for this socket. + * @param endpoint endpoint to assign. + * @return 0 on success, -1 on failure. + * @note for a datagram socket ::connect only sets the default peer, it + * does not perform any handshake, so there is no connecting state. + */ + int connect (const Endpoint& endpoint) noexcept + { + if ((this->_state != State::Closed) && (this->_state != State::Disconnected)) + { + lastError = make_error_code (Errc::InUse); + return -1; + } + + if ((this->_state == State::Closed) && (open (endpoint.protocol ()) == -1)) + { + return -1; + } + + if (::connect (this->_handle, endpoint.addr (), endpoint.length ()) == -1) + { + lastError = std::error_code (errno, std::generic_category ()); + close (); + return -1; + } + + _remote = endpoint; + this->_state = State::Connected; + + return 0; + } + + /** + * @brief remove the default remote endpoint. + * @return 0 on success, -1 on failure. + */ + int disconnect () noexcept + { + if (this->_state == State::Connected) + { + struct sockaddr_storage nullAddr; + ::memset (&nullAddr, 0, sizeof (nullAddr)); + + nullAddr.ss_family = AF_UNSPEC; + + int result = ::connect (this->_handle, reinterpret_cast (&nullAddr), + sizeof (struct sockaddr_storage)); + if (result == -1) + { + if (errno != EAFNOSUPPORT) + { + lastError = std::error_code (errno, std::generic_category ()); + return -1; + } + } + + this->_state = State::Disconnected; + _remote = {}; + } + + return 0; + } + + /** + * @brief close the socket handle. + */ + void close () noexcept override + { + BasicSocket::close (); + _remote = {}; + } + + /** + * @brief read data on the socket. + * @param data buffer used to store the data received. + * @param maxSize maximum number of bytes to read. + * @param endpoint endpoint from where data are coming (optional). + * @return The number of bytes received, -1 on failure. + */ + int readFrom (char* data, unsigned long maxSize, Endpoint* endpoint = nullptr) noexcept + { + struct sockaddr_storage sa; + socklen_t sa_len = sizeof (struct sockaddr_storage); + + int size = ::recvfrom (this->_handle, data, maxSize, 0, reinterpret_cast (&sa), &sa_len); + if (size < 1) + { + if (size == -1) + { + lastError = std::error_code (errno, std::generic_category ()); + } + else + { + lastError = make_error_code (Errc::ConnectionClosed); + this->_state = State::Disconnected; + } + + return -1; + } + + if (endpoint != nullptr) + { + *endpoint = Endpoint (reinterpret_cast (&sa), sa_len); + } + + return size; + } + + /** + * @brief write data on the socket. + * @param data data buffer to send. + * @param maxSize maximum number of bytes to write. + * @param endpoint endpoint where to write the data. + * @return the number of bytes written, -1 on failure. + */ + int writeTo (const char* data, unsigned long maxSize, const Endpoint& endpoint) noexcept + { + if ((this->_state == State::Closed) && (open (endpoint.protocol ()) == -1)) + { + return -1; + } + + int result = ::sendto (this->_handle, data, maxSize, 0, endpoint.addr (), endpoint.length ()); + if (result < 0) + { + lastError = std::error_code (errno, std::generic_category ()); + return -1; + } + + return result; + } + + /** + * @brief determine the remote endpoint associated with this socket. + * @return remote endpoint. + */ + const Endpoint& remoteEndpoint () const noexcept + { + return _remote; + } + + /** + * @brief check if the socket is connected. + * @return true if connected, false otherwise. + */ + bool connected () const noexcept + { + return (this->_state == State::Connected); + } + + /** + * @brief get socket mtu. + * @return mtu on success, -1 on failure. + */ + int mtu () const noexcept + { + if (this->_state == State::Closed) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + int result = -1, value = -1; + socklen_t valueLen = sizeof (value); + + if (this->_protocol.family () == AF_INET6) + { + result = ::getsockopt (this->_handle, IPPROTO_IPV6, IPV6_MTU, &value, &valueLen); + } + else if (this->_protocol.family () == AF_INET) + { + result = ::getsockopt (this->_handle, IPPROTO_IP, IP_MTU, &value, &valueLen); + } + else + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + if (result == -1) + { + lastError = std::error_code (errno, std::generic_category ()); + return -1; + } + + return value; + } + + /** + * @brief returns the Time-To-Live value. + * @return The Time-To-Live value. + */ + int ttl () const noexcept + { + return _ttl; + } + + protected: + /// remote endpoint. + Endpoint _remote; + + /// packet time to live. + int _ttl = 60; + }; + + /** + * @brief compare if socket handle is inferior. + * @param a socket handle to compare. + * @param b socket handle to compare to. + * @return true if inferior. + */ + template + inline bool operator< (const BasicDatagramSocket& a, const BasicDatagramSocket& b) noexcept + { + return a.handle () < b.handle (); + } +} + +#endif diff --git a/core/include/join/endpoint.hpp b/core/include/join/endpoint.hpp index 38844215..cf76a73b 100644 --- a/core/include/join/endpoint.hpp +++ b/core/include/join/endpoint.hpp @@ -26,7 +26,7 @@ #define JOIN_CORE_ENDPOINT_HPP // libjoin. -#include +#include // C++. #include diff --git a/core/include/join/ipaddress.hpp b/core/include/join/ip_address.hpp similarity index 99% rename from core/include/join/ipaddress.hpp rename to core/include/join/ip_address.hpp index c8043ffd..4a45956c 100644 --- a/core/include/join/ipaddress.hpp +++ b/core/include/join/ip_address.hpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#ifndef JOIN_CORE_IPADDRESS_HPP -#define JOIN_CORE_IPADDRESS_HPP +#ifndef JOIN_CORE_IP_ADDRESS_HPP +#define JOIN_CORE_IP_ADDRESS_HPP // C++. #include diff --git a/core/include/join/macaddress.hpp b/core/include/join/mac_address.hpp similarity index 99% rename from core/include/join/macaddress.hpp rename to core/include/join/mac_address.hpp index ee9e2445..e877454a 100644 --- a/core/include/join/macaddress.hpp +++ b/core/include/join/mac_address.hpp @@ -22,11 +22,11 @@ * SOFTWARE. */ -#ifndef JOIN_CORE_MACADDRESS_HPP -#define JOIN_CORE_MACADDRESS_HPP +#ifndef JOIN_CORE_MAC_ADDRESS_HPP +#define JOIN_CORE_MAC_ADDRESS_HPP // libjoin. -#include +#include // C++ #include diff --git a/core/include/join/protocol.hpp b/core/include/join/protocol.hpp index 0e888e70..9aef3e57 100644 --- a/core/include/join/protocol.hpp +++ b/core/include/join/protocol.hpp @@ -29,7 +29,6 @@ #include // C. -#include #include namespace join @@ -194,7 +193,7 @@ namespace join * @brief construct the udp protocol instance. * @param family IP address family. */ - constexpr Udp (int family = AF_INET) noexcept + constexpr explicit Udp (int family = AF_INET) noexcept : _family (family) { } @@ -286,7 +285,7 @@ namespace join * @brief create the icmp protocol instance. * @param family IP address family. */ - constexpr Icmp (int family = AF_INET) noexcept + constexpr explicit Icmp (int family = AF_INET) noexcept : _family (family) { } @@ -385,7 +384,7 @@ namespace join * @brief create the tcp protocol instance. * @param family IP address family. */ - constexpr Tcp (int family = AF_INET) noexcept + constexpr explicit Tcp (int family = AF_INET) noexcept : _family (family) { } diff --git a/core/include/join/socket.hpp b/core/include/join/socket.hpp index dcbd4231..53e4a513 100644 --- a/core/include/join/socket.hpp +++ b/core/include/join/socket.hpp @@ -31,20 +31,15 @@ #include #include -// Libraries. -#include - // C++. #include -#include +#include +#include // C. #include #include #include -#include -#include -#include #include #include @@ -58,7 +53,6 @@ namespace join { public: using Ptr = std::unique_ptr>; - using Proto = Protocol; using Endpoint = typename Protocol::Endpoint; /** @@ -118,7 +112,7 @@ namespace join * @brief create socket instance specifying the mode. * @param mode blocking mode. */ - BasicSocket (Mode mode) + explicit BasicSocket (Mode mode) : _mode (mode) { } @@ -140,7 +134,7 @@ namespace join * @brief move constructor. * @param other other object to move. */ - BasicSocket (BasicSocket&& other) + BasicSocket (BasicSocket&& other) noexcept : _state (other._state) , _mode (other._mode) , _handle (other._handle) @@ -157,14 +151,14 @@ namespace join * @param other other object to assign. * @return current object. */ - BasicSocket& operator= (BasicSocket&& other) + BasicSocket& operator= (BasicSocket&& other) noexcept { - this->close (); + close (); - this->_state = other._state; - this->_mode = other._mode; - this->_handle = other._handle; - this->_protocol = other._protocol; + _state = other._state; + _mode = other._mode; + _handle = other._handle; + _protocol = other._protocol; other._state = State::Closed; other._mode = Mode::NonBlocking; @@ -179,9 +173,9 @@ namespace join */ virtual ~BasicSocket () { - if (this->_handle != -1) + if (_handle != -1) { - ::close (this->_handle); + ::close (_handle); } } @@ -192,26 +186,30 @@ namespace join */ virtual int open (const Protocol& protocol = Protocol ()) noexcept { - if (this->_state != State::Closed) + if (_state != State::Closed) { lastError = make_error_code (Errc::InUse); return -1; } - if (this->_mode == Mode::NonBlocking) - this->_handle = ::socket (protocol.family (), protocol.type () | SOCK_NONBLOCK, protocol.protocol ()); + if (_mode == Mode::NonBlocking) + { + _handle = ::socket (protocol.family (), protocol.type () | SOCK_NONBLOCK, protocol.protocol ()); + } else - this->_handle = ::socket (protocol.family (), protocol.type (), protocol.protocol ()); + { + _handle = ::socket (protocol.family (), protocol.type (), protocol.protocol ()); + } - if (this->_handle == -1) + if (_handle == -1) { lastError = std::error_code (errno, std::generic_category ()); - this->close (); + close (); return -1; } - this->_state = State::Disconnected; - this->_protocol = protocol; + _state = State::Disconnected; + _protocol = protocol; return 0; } @@ -221,11 +219,11 @@ namespace join */ virtual void close () noexcept { - if (this->_state != State::Closed) + if (_state != State::Closed) { - ::close (this->_handle); - this->_state = State::Closed; - this->_handle = -1; + ::close (_handle); + _state = State::Closed; + _handle = -1; } } @@ -234,9 +232,9 @@ namespace join * @param endpoint endpoint to assign to the socket. * @return 0 on success, -1 on failure. */ - virtual int bind (const Endpoint& endpoint) noexcept + int bind (const Endpoint& endpoint) noexcept { - if ((this->_state == State::Closed) && (this->open (endpoint.protocol ()) == -1)) + if ((_state == State::Closed) && (open (endpoint.protocol ()) == -1)) { return -1; } @@ -251,14 +249,48 @@ namespace join } else if ((endpoint.protocol ().family () == AF_INET6) || (endpoint.protocol ().family () == AF_INET)) { - this->setOption (Option::ReuseAddr, 1); + setOption (Option::ReuseAddr, 1); } else if (endpoint.protocol ().family () == AF_UNIX) { ::unlink (endpoint.device ().c_str ()); } - if (::bind (this->_handle, endpoint.addr (), endpoint.length ()) == -1) + if (::bind (_handle, endpoint.addr (), endpoint.length ()) == -1) + { + lastError = std::error_code (errno, std::generic_category ()); + return -1; + } + + return 0; + } + + /** + * @brief assigns the specified device to the socket. + * @param device device name. + * @return 0 on success, -1 on failure. + */ + int bindToDevice (const std::string& device) noexcept + { + if (_state == State::Closed) + { + lastError = make_error_code (Errc::ConnectionClosed); + return -1; + } + + if (_state == State::Connected) + { + lastError = make_error_code (Errc::InUse); + return -1; + } + + if ((_protocol.family () == AF_INET6) || (_protocol.family () == AF_INET)) + { + setOption (Option::ReuseAddr, 1); + } + + int result = ::setsockopt (_handle, SOL_SOCKET, SO_BINDTODEVICE, device.c_str (), device.size ()); + if (result == -1) { lastError = std::error_code (errno, std::generic_category ()); return -1; @@ -271,12 +303,12 @@ namespace join * @brief get the number of readable bytes. * @return the number of readable bytes, -1 on failure. */ - virtual int canRead () const noexcept + int canRead () const noexcept { int available = 0; // check if data can be read in the socket internal buffer. - if (::ioctl (this->_handle, FIONREAD, &available) == -1) + if (::ioctl (_handle, FIONREAD, &available) == -1) { lastError = std::error_code (errno, std::generic_category ()); return -1; @@ -290,9 +322,9 @@ namespace join * @param timeout timeout in milliseconds. * @return true if there is new data available for reading, false otherwise. */ - virtual bool waitReadyRead (int timeout = 0) const noexcept + bool waitReadyRead (int timeout = 0) const noexcept { - return (this->wait (true, false, timeout) == 0); + return (wait (true, false, timeout) == 0); } /** @@ -301,7 +333,7 @@ namespace join * @param maxSize maximum number of bytes to read. * @return the number of bytes received, -1 on failure. */ - virtual int read (char* data, unsigned long maxSize) noexcept + int read (char* data, unsigned long maxSize) noexcept { struct iovec iov; iov.iov_base = data; @@ -315,7 +347,7 @@ namespace join message.msg_control = nullptr; message.msg_controllen = 0; - int size = ::recvmsg (this->_handle, &message, 0); + int size = ::recvmsg (_handle, &message, 0); if (size < 1) { if (size == -1) @@ -337,9 +369,9 @@ namespace join * @param timeout timeout in milliseconds. * @return true if data can be written, false otherwise. */ - virtual bool waitReadyWrite (int timeout = 0) const noexcept + bool waitReadyWrite (int timeout = 0) const noexcept { - return (this->wait (false, true, timeout) == 0); + return (wait (false, true, timeout) == 0); } /** @@ -348,7 +380,7 @@ namespace join * @param maxSize maximum number of bytes to write. * @return the number of bytes written, -1 on failure. */ - virtual int write (const char* data, unsigned long maxSize) noexcept + int write (const char* data, unsigned long maxSize) noexcept { struct iovec iov; iov.iov_base = const_cast (data); @@ -362,7 +394,7 @@ namespace join message.msg_control = nullptr; message.msg_controllen = 0; - int result = ::sendmsg (this->_handle, &message, 0); + int result = ::sendmsg (_handle, &message, 0); if (result == -1) { lastError = std::error_code (errno, std::generic_category ()); @@ -378,13 +410,13 @@ namespace join */ void setMode (Mode mode) noexcept { - this->_mode = mode; + _mode = mode; - if (this->_state != State::Closed) + if (_state != State::Closed) { - int flags = ::fcntl (this->_handle, F_GETFL, 0); + int flags = ::fcntl (_handle, F_GETFL, 0); - if (this->_mode == Mode::NonBlocking) + if (_mode == Mode::NonBlocking) { flags = flags | O_NONBLOCK; } @@ -393,7 +425,7 @@ namespace join flags = flags & ~O_NONBLOCK; } - ::fcntl (this->_handle, F_SETFL, flags); + ::fcntl (_handle, F_SETFL, flags); } } @@ -449,12 +481,77 @@ namespace join optname = PACKET_AUXDATA; break; + case Option::Ttl: + if (family () == AF_INET6) + { + optlevel = IPPROTO_IPV6; + optname = IPV6_UNICAST_HOPS; + } + else + { + optlevel = IPPROTO_IP; + optname = IP_TTL; + } + break; + + case Option::MulticastLoop: + if (family () == AF_INET6) + { + optlevel = IPPROTO_IPV6; + optname = IPV6_MULTICAST_LOOP; + } + else + { + optlevel = IPPROTO_IP; + optname = IP_MULTICAST_LOOP; + } + break; + + case Option::MulticastTtl: + if (family () == AF_INET6) + { + optlevel = IPPROTO_IPV6; + optname = IPV6_MULTICAST_HOPS; + } + else + { + optlevel = IPPROTO_IP; + optname = IP_MULTICAST_TTL; + } + break; + + case Option::PathMtuDiscover: + if (family () == AF_INET6) + { + optlevel = IPPROTO_IPV6; + optname = IPV6_MTU_DISCOVER; + } + else + { + optlevel = IPPROTO_IP; + optname = IP_MTU_DISCOVER; + } + break; + + case Option::RcvError: + if (family () == AF_INET6) + { + optlevel = IPPROTO_IPV6; + optname = IPV6_RECVERR; + } + else + { + optlevel = IPPROTO_IP; + optname = IP_RECVERR; + } + break; + default: lastError = make_error_code (Errc::InvalidParam); return -1; } - int result = ::setsockopt (this->_handle, optlevel, optname, &value, sizeof (value)); + int result = ::setsockopt (_handle, optlevel, optname, &value, sizeof (value)); if (result == -1) { lastError = std::error_code (errno, std::generic_category ()); @@ -473,7 +570,7 @@ namespace join struct sockaddr_storage sa; socklen_t sa_len = sizeof (struct sockaddr_storage); - if (::getsockname (this->_handle, reinterpret_cast (&sa), &sa_len) == -1) + if (::getsockname (_handle, reinterpret_cast (&sa), &sa_len) == -1) { return {}; } @@ -487,16 +584,7 @@ namespace join */ bool opened () const noexcept { - return (this->_state != State::Closed); - } - - /** - * @brief check if the socket is secure. - * @return true if encrypted, false otherwise. - */ - virtual bool encrypted () const noexcept - { - return false; + return (_state != State::Closed); } /** @@ -505,7 +593,7 @@ namespace join */ int family () const noexcept { - return this->_protocol.family (); + return _protocol.family (); } /** @@ -514,7 +602,7 @@ namespace join */ int type () const noexcept { - return this->_protocol.type (); + return _protocol.type (); } /** @@ -523,7 +611,7 @@ namespace join */ int protocol () const noexcept { - return this->_protocol.protocol (); + return _protocol.protocol (); } /** @@ -532,7 +620,7 @@ namespace join */ int handle () const noexcept { - return this->_handle; + return _handle; } /** @@ -567,7 +655,6 @@ namespace join return static_cast (~sum); } - protected: /** * @brief wait for the socket handle to become ready. * @param wantRead set to true if want read @@ -577,7 +664,10 @@ namespace join */ int wait (bool wantRead, bool wantWrite, int timeout) const noexcept { - struct pollfd handle = {.fd = this->_handle, .events = 0, .revents = 0}; + struct pollfd handle; + handle.fd = _handle; + handle.events = 0; + handle.revents = 0; if (wantRead) { @@ -611,6 +701,7 @@ namespace join return 0; } + protected: /// socket state. State _state = State::Closed; @@ -622,10 +713,6 @@ namespace join /// protocol. Protocol _protocol; - - /// friendship with TLS wrapper. - template - friend class TlsWrapper; }; /** @@ -635,865 +722,7 @@ namespace join * @return true if inferior. */ template - constexpr bool operator< (const BasicSocket& a, const BasicSocket& b) noexcept - { - return a.handle () < b.handle (); - } - - /** - * @brief basic datagram socket class. - */ - template - class BasicDatagramSocket : public BasicSocket - { - public: - using Ptr = std::unique_ptr>; - using Proto = Protocol; - using Mode = typename BasicSocket::Mode; - using Option = typename BasicSocket::Option; - using State = typename BasicSocket::State; - using Endpoint = typename Protocol::Endpoint; - - /** - * @brief Default constructor. - */ - BasicDatagramSocket (int ttl = 60) - : BasicDatagramSocket (Mode::NonBlocking, ttl) - { - } - - /** - * @brief Create instance specifying the mode. - * @param mode Set the socket blocking mode. - */ - BasicDatagramSocket (Mode mode, int ttl = 60) - : BasicSocket (mode) - , _ttl (ttl) - { - } - - /** - * @brief Copy constructor. - * @param other Other object to copy. - */ - BasicDatagramSocket (const BasicDatagramSocket& other) = delete; - - /** - * @brief Copy assignment operator. - * @param other other object to assign. - * @return assigned object. - */ - BasicDatagramSocket& operator= (const BasicDatagramSocket& other) = delete; - - /** - * @brief Move constructor. - * @param other Other object to move. - */ - BasicDatagramSocket (BasicDatagramSocket&& other) - : BasicSocket (std::move (other)) - , _remote (std::move (other._remote)) - , _ttl (other._ttl) - { - other._ttl = 60; - } - - /** - * @brief Move assignment operator. - * @param other other object to assign. - * @return assigned object. - */ - BasicDatagramSocket& operator= (BasicDatagramSocket&& other) - { - BasicSocket::operator= (std::move (other)); - - _remote = std::move (other._remote); - _ttl = other._ttl; - - other._ttl = 60; - - return *this; - } - - /** - * @brief Destroy the instance. - */ - virtual ~BasicDatagramSocket () = default; - - /** - * @brief open socket using the given protocol. - * @param protocol protocol to use. - * @return 0 on success, -1 on failure. - */ - virtual int open (const Protocol& protocol = Protocol ()) noexcept override - { - int result = BasicSocket::open (protocol); - if (result == -1) - { - return -1; - } - - int off = 0; - - if ((protocol.protocol () == IPPROTO_UDP) || (protocol.protocol () == IPPROTO_TCP)) - { - if ((protocol.family () == AF_INET6) && - (::setsockopt (this->_handle, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof (off)) == -1)) - { - lastError = std::error_code (errno, std::generic_category ()); - this->close (); - return -1; - } - } - - if ((protocol.protocol () == IPPROTO_ICMPV6) || (protocol.protocol () == IPPROTO_ICMP)) - { - if ((protocol.family () == AF_INET) && - (::setsockopt (this->_handle, IPPROTO_IP, IP_HDRINCL, &off, sizeof (off)) == -1)) - { - lastError = std::error_code (errno, std::generic_category ()); - this->close (); - return -1; - } - - this->setOption (Option::MulticastTtl, this->_ttl); - this->setOption (Option::Ttl, this->_ttl); - } - - return 0; - } - - /** - * @brief assigns the specified device to the socket. - * @param device device name. - * @return 0 on success, -1 on failure. - */ - virtual int bindToDevice (const std::string& device) noexcept - { - if (this->_state == State::Closed) - { - lastError = make_error_code (Errc::ConnectionClosed); - return -1; - } - - if (this->_state == State::Connected) - { - lastError = make_error_code (Errc::InUse); - return -1; - } - - if ((this->_protocol.family () == AF_INET6) || (this->_protocol.family () == AF_INET)) - { - this->setOption (Option::ReuseAddr, 1); - } - - int result = setsockopt (this->_handle, SOL_SOCKET, SO_BINDTODEVICE, device.c_str (), device.size ()); - if (result == -1) - { - lastError = std::error_code (errno, std::generic_category ()); - return -1; - } - - return 0; - } - - /** - * @brief make a connection to the given endpoint. - * @param endpoint endpoint to connect to. - * @return 0 on success, -1 on failure. - */ - virtual int connect (const Endpoint& endpoint) - { - if ((this->_state != State::Closed) && (this->_state != State::Disconnected)) - { - lastError = make_error_code (Errc::InUse); - return -1; - } - - if ((this->_state == State::Closed) && (this->open (endpoint.protocol ()) == -1)) - { - return -1; - } - - int result = ::connect (this->_handle, endpoint.addr (), endpoint.length ()); - - this->_state = State::Connecting; - this->_remote = endpoint; - - if (result == -1) - { - lastError = std::error_code (errno, std::generic_category ()); - if (lastError != std::errc::operation_in_progress) - { - this->close (); - } - return -1; - } - - this->_state = State::Connected; - - return 0; - } - - /** - * @brief shutdown the connection. - * @return 0 on success, -1 on failure. - */ - virtual int disconnect () - { - if (this->_state == State::Connected) - { - struct sockaddr_storage nullAddr; - ::memset (&nullAddr, 0, sizeof (nullAddr)); - - nullAddr.ss_family = AF_UNSPEC; - - int result = ::connect (this->_handle, reinterpret_cast (&nullAddr), - sizeof (struct sockaddr_storage)); - if (result == -1) - { - if (errno != EAFNOSUPPORT) - { - lastError = std::error_code (errno, std::generic_category ()); - return -1; - } - } - - this->_state = State::Disconnected; - this->_remote = {}; - } - - return 0; - } - - /** - * @brief close the socket handle. - */ - virtual void close () noexcept override - { - BasicSocket::close (); - this->_remote = {}; - } - - /** - * @brief read data. - * @param data buffer used to store the data received. - * @param maxSize maximum number of bytes to read. - * @return the number of bytes received, -1 on failure. - */ - virtual int read (char* data, unsigned long maxSize) noexcept override - { - return BasicSocket::read (data, maxSize); - } - - /** - * @brief read data on the socket. - * @param data buffer used to store the data received. - * @param maxSize maximum number of bytes to read. - * @param endpoint endpoint from where data are coming (optional). - * @return The number of bytes received, -1 on failure. - */ - virtual int readFrom (char* data, unsigned long maxSize, Endpoint* endpoint = nullptr) noexcept - { - struct sockaddr_storage sa; - socklen_t sa_len = sizeof (struct sockaddr_storage); - - int size = ::recvfrom (this->_handle, data, maxSize, 0, reinterpret_cast (&sa), &sa_len); - if (size < 1) - { - if (size == -1) - { - lastError = std::error_code (errno, std::generic_category ()); - } - else - { - lastError = make_error_code (Errc::ConnectionClosed); - this->_state = State::Disconnected; - } - - return -1; - } - - if (endpoint != nullptr) - { - *endpoint = Endpoint (reinterpret_cast (&sa), sa_len); - } - - return size; - } - - /** - * @brief write data. - * @param data data buffer to send. - * @param maxSize maximum number of bytes to write. - * @return the number of bytes written, -1 on failure. - */ - virtual int write (const char* data, unsigned long maxSize) noexcept override - { - return BasicSocket::write (data, maxSize); - } - - /** - * @brief write data on the socket. - * @param data data buffer to send. - * @param maxSize maximum number of bytes to write. - * @param endpoint endpoint where to write the data. - * @return the number of bytes written, -1 on failure. - */ - virtual int writeTo (const char* data, unsigned long maxSize, const Endpoint& endpoint) noexcept - { - if ((this->_state == State::Closed) && (this->open (endpoint.protocol ()) == -1)) - { - return -1; - } - - int result = ::sendto (this->_handle, data, maxSize, 0, endpoint.addr (), endpoint.length ()); - if (result < 0) - { - lastError = std::error_code (errno, std::generic_category ()); - return -1; - } - - return result; - } - - /** - * @brief set the given option to the given value. - * @param option socket option. - * @param value option value. - * @return 0 on success, -1 on failure. - */ - virtual int setOption (Option option, int value) noexcept override - { - if (this->_state == State::Closed) - { - lastError = make_error_code (Errc::OperationFailed); - return -1; - } - - int optlevel, optname; - - switch (option) - { - case Option::Ttl: - if (this->family () == AF_INET6) - { - optlevel = IPPROTO_IPV6; - optname = IPV6_UNICAST_HOPS; - } - else - { - optlevel = IPPROTO_IP; - optname = IP_TTL; - } - break; - - case Option::MulticastLoop: - if (this->family () == AF_INET6) - { - optlevel = IPPROTO_IPV6; - optname = IPV6_MULTICAST_LOOP; - } - else - { - optlevel = IPPROTO_IP; - optname = IP_MULTICAST_LOOP; - } - break; - - case Option::MulticastTtl: - if (this->family () == AF_INET6) - { - optlevel = IPPROTO_IPV6; - optname = IPV6_MULTICAST_HOPS; - } - else - { - optlevel = IPPROTO_IP; - optname = IP_MULTICAST_TTL; - } - break; - - case Option::PathMtuDiscover: - if (this->family () == AF_INET6) - { - optlevel = IPPROTO_IPV6; - optname = IPV6_MTU_DISCOVER; - } - else - { - optlevel = IPPROTO_IP; - optname = IP_MTU_DISCOVER; - } - break; - - case Option::RcvError: - if (this->family () == AF_INET6) - { - optlevel = IPPROTO_IPV6; - optname = IPV6_RECVERR; - } - else - { - optlevel = IPPROTO_IP; - optname = IP_RECVERR; - } - break; - - default: - return BasicSocket::setOption (option, value); - } - - int result = ::setsockopt (this->_handle, optlevel, optname, &value, sizeof (value)); - if (result == -1) - { - lastError = std::error_code (errno, std::generic_category ()); - return -1; - } - - return 0; - } - - /** - * @brief determine the remote endpoint associated with this socket. - * @return remote endpoint. - */ - const Endpoint& remoteEndpoint () const noexcept - { - return this->_remote; - } - - /** - * @brief check if the socket is connecting. - * @return true if connecting, false otherwise. - */ - virtual bool connecting () const noexcept - { - return (this->_state == State::Connecting); - } - - /** - * @brief check if the socket is connected. - * @return true if connected, false otherwise. - */ - virtual bool connected () noexcept - { - return (this->_state == State::Connected); - } - - /** - * @brief get socket mtu. - * @return mtu on success, -1 on failure. - */ - int mtu () const - { - if (this->_state == State::Closed) - { - lastError = make_error_code (Errc::OperationFailed); - return -1; - } - - int result = -1, value = -1; - socklen_t valueLen = sizeof (value); - - if (this->_protocol.family () == AF_INET6) - { - result = ::getsockopt (this->_handle, IPPROTO_IPV6, IPV6_MTU, &value, &valueLen); - } - else if (this->_protocol.family () == AF_INET) - { - result = ::getsockopt (this->_handle, IPPROTO_IP, IP_MTU, &value, &valueLen); - } - else - { - lastError = make_error_code (Errc::OperationFailed); - return -1; - } - - if (result == -1) - { - lastError = std::error_code (errno, std::generic_category ()); - return -1; - } - - return value; - } - - /** - * @brief returns the Time-To-Live value. - * @return The Time-To-Live value. - */ - int ttl () const noexcept - { - return this->_ttl; - } - - protected: - /// remote endpoint. - Endpoint _remote; - - /// packet time to live. - int _ttl = 60; - }; - - /** - * @brief compare if socket handle is inferior. - * @param a socket handle to compare. - * @param b socket handle to compare to. - * @return true if inferior. - */ - template - constexpr bool operator< (const BasicDatagramSocket& a, const BasicDatagramSocket& b) noexcept - { - return a.handle () < b.handle (); - } - - /** - * @brief basic stream socket class. - */ - template - class BasicStreamSocket : public BasicDatagramSocket - { - public: - using Ptr = std::unique_ptr>; - using Proto = Protocol; - using Mode = typename BasicDatagramSocket::Mode; - using Option = typename BasicDatagramSocket::Option; - using State = typename BasicDatagramSocket::State; - using Endpoint = typename Protocol::Endpoint; - - /** - * @brief default constructor. - */ - BasicStreamSocket () - : BasicStreamSocket (Mode::NonBlocking) - { - } - - /** - * @brief create instance specifying the mode. - * @param mode Set the socket blocking mode. - */ - BasicStreamSocket (Mode mode) - : BasicDatagramSocket (mode) - { - } - - /** - * @brief copy constructor. - * @param other other object to copy. - */ - BasicStreamSocket (const BasicStreamSocket& other) = delete; - - /** - * @brief copy assignment operator. - * @param other other object to assign. - * @return assigned object. - */ - BasicStreamSocket& operator= (const BasicStreamSocket& other) = delete; - - /** - * @brief move constructor. - * @param other other object to move. - */ - BasicStreamSocket (BasicStreamSocket&& other) - : BasicDatagramSocket (std::move (other)) - { - } - - /** - * @brief move assignment operator. - * @param other other object to assign. - * @return assigned object. - */ - BasicStreamSocket& operator= (BasicStreamSocket&& other) - { - BasicDatagramSocket::operator= (std::move (other)); - - return *this; - } - - /** - * @brief destroy the instance. - */ - virtual ~BasicStreamSocket () = default; - - /** - * @brief block until connected. - * @param timeout timeout in milliseconds. - * @return true if connected, false otherwise. - */ - virtual bool waitConnected (int timeout = 0) - { - if (this->_state != State::Connected) - { - if (this->_state != State::Connecting) - { - lastError = make_error_code (Errc::OperationFailed); - return false; - } - - if (!this->waitReadyWrite (timeout)) - { - return false; - } - - return connected (); - } - - return true; - } - - /** - * @brief shutdown the connection. - * @return 0 on success, -1 on failure. - */ - virtual int disconnect () override - { - if (this->_state == State::Connected) - { - ::shutdown (this->_handle, SHUT_WR); - this->_state = State::Disconnecting; - } - - if (this->_state == State::Disconnecting) - { - char buffer[4096]; - // closing before reading can make the client - // not see all of our output. - // we have to do a "lingering close" - for (;;) - { - int result = this->read (buffer, sizeof (buffer)); - if (result <= 0) - { - if ((result == -1) && (lastError == Errc::TemporaryError)) - { - return -1; - } - - break; - } - } - - ::shutdown (this->_handle, SHUT_RD); - this->_state = State::Disconnected; - } - - return 0; - } - - /** - * @brief wait until the connection as been shut down. - * @param timeout timeout in milliseconds. - * return true if the connection as been shut down, false otherwise. - */ - virtual bool waitDisconnected (int timeout = 0) - { - if ((this->_state != State::Disconnected) && (this->_state != State::Closed)) - { - if (this->_state != State::Disconnecting) - { - lastError = make_error_code (Errc::OperationFailed); - return false; - } - - auto start = std::chrono::steady_clock::now (); - int elapsed = 0; - - while ((lastError == Errc::TemporaryError) && (elapsed <= timeout)) - { - if (!this->waitReadyRead (timeout - elapsed)) - { - return false; - } - - if (this->disconnect () == 0) - { - return true; - } - - if (timeout) - { - elapsed = std::chrono::duration_cast ( - std::chrono::steady_clock::now () - start) - .count (); - } - } - - return false; - } - - return true; - } - - /** - * @brief read data until size is reached or an error occurred. - * @param data buffer used to store the data received. - * @param size number of bytes to read. - * @param timeout timeout in milliseconds. - * @return 0 on success, -1 on failure. - */ - int readExactly (char* data, unsigned long size, int timeout = 0) - { - unsigned long numRead = 0; - - while (numRead < size) - { - int result = this->read (data + numRead, size - numRead); - if (result == -1) - { - if (lastError == Errc::TemporaryError) - { - if (this->waitReadyRead (timeout)) - continue; - } - - return -1; - } - - numRead += result; - } - - return 0; - } - - /** - * @brief read data until size is reached or an error occurred. - * @param data buffer used to store the data received. - * @param size number of bytes to read. - * @param timeout timeout in milliseconds. - * @return 0 on success, -1 on failure. - */ - int readExactly (std::string& data, unsigned long size, int timeout = 0) - { - data.resize (size); - return readExactly (&data[0], size, timeout); - } - - /** - * @brief write data until size is reached or an error occurred. - * @param data data buffer to send. - * @param size number of bytes to write. - * @param timeout timeout in milliseconds. - * @return 0 on success, -1 on failure. - */ - int writeExactly (const char* data, unsigned long size, int timeout = 0) - { - unsigned long numWrite = 0; - - while (numWrite < size) - { - int result = this->write (data + numWrite, size - numWrite); - if (result == -1) - { - if (lastError == Errc::TemporaryError) - { - if (this->waitReadyWrite (timeout)) - continue; - } - - return -1; - } - - numWrite += result; - } - - return 0; - } - - /** - * @brief set the given option to the given value. - * @param option socket option. - * @param value option value. - * @return 0 on success, -1 on failure. - */ - virtual int setOption (Option option, int value) noexcept override - { - if (this->_state == State::Closed) - { - lastError = make_error_code (Errc::OperationFailed); - return -1; - } - - int optlevel, optname; - - switch (option) - { - case Option::NoDelay: - optlevel = IPPROTO_TCP; - optname = TCP_NODELAY; - break; - - case Option::KeepIdle: - optlevel = IPPROTO_TCP; - optname = TCP_KEEPIDLE; - break; - - case Option::KeepIntvl: - optlevel = IPPROTO_TCP; - optname = TCP_KEEPINTVL; - break; - - case Option::KeepCount: - optlevel = IPPROTO_TCP; - optname = TCP_KEEPCNT; - break; - - default: - return BasicDatagramSocket::setOption (option, value); - } - - int result = ::setsockopt (this->_handle, optlevel, optname, &value, sizeof (value)); - if (result == -1) - { - lastError = std::error_code (errno, std::generic_category ()); - return -1; - } - - return 0; - } - - /** - * @brief check if the socket is connected. - * @return true if connected, false otherwise. - */ - virtual bool connected () noexcept override - { - if (this->_state == State::Connected) - { - return true; - } - else if (this->_state != State::Connecting) - { - return false; - } - - int optval; - socklen_t optlen = sizeof (optval); - - int result = ::getsockopt (this->_handle, SOL_SOCKET, SO_ERROR, &optval, &optlen); - if ((result == -1) || (optval != 0)) - { - return false; - } - - this->_state = State::Connected; - - return true; - } - - /// friendship with basic stream acceptor - friend class BasicStreamAcceptor; - }; - - /** - * @brief compare if socket handle is inferior. - * @param a socket handle to compare. - * @param b socket handle to compare to. - * @return true if inferior. - */ - template - constexpr bool operator< (const BasicStreamSocket& a, const BasicStreamSocket& b) noexcept + inline bool operator< (const BasicSocket& a, const BasicSocket& b) noexcept { return a.handle () < b.handle (); } diff --git a/core/include/join/socketstream.hpp b/core/include/join/socket_stream.hpp similarity index 77% rename from core/include/join/socketstream.hpp rename to core/include/join/socket_stream.hpp index 29d27c8d..f0718ad7 100644 --- a/core/include/join/socketstream.hpp +++ b/core/include/join/socket_stream.hpp @@ -22,11 +22,11 @@ * SOFTWARE. */ -#ifndef JOIN_CORE_SOCKETSTREAM_HPP -#define JOIN_CORE_SOCKETSTREAM_HPP +#ifndef JOIN_CORE_SOCKET_STREAM_HPP +#define JOIN_CORE_SOCKET_STREAM_HPP // libjoin. -#include +#include // C++. #include @@ -55,6 +55,16 @@ namespace join { } + /** + * @brief construct the socket stream buffer by moving an existing socket in. + * @param socket socket to move in. + */ + explicit BasicSocketStreambuf (Socket&& socket) + : _buf (std::make_unique (2 * _bufsize)) + , _socket (std::move (socket)) + { + } + /** * @brief copy constructor. * @param other other object to copy. @@ -87,12 +97,12 @@ namespace join */ BasicSocketStreambuf& operator= (BasicSocketStreambuf&& other) { - this->close (); + close (); std::streambuf::operator= (std::move (other)); - this->_buf = std::move (other._buf); - this->_timeout = other._timeout; - this->_socket = std::move (other._socket); + _buf = std::move (other._buf); + _timeout = other._timeout; + _socket = std::move (other._socket); return *this; } @@ -102,9 +112,9 @@ namespace join */ virtual ~BasicSocketStreambuf () { - if (this->_socket.connected ()) + if (_socket.connected ()) { - this->overflow (traits_type::eof ()); + overflow (traits_type::eof ()); } } @@ -115,7 +125,7 @@ namespace join */ BasicSocketStreambuf* bind (const Endpoint& endpoint) { - if (this->_socket.bind (endpoint) == -1) + if (_socket.bind (endpoint) == -1) { return nullptr; } @@ -130,16 +140,16 @@ namespace join */ BasicSocketStreambuf* connect (const Endpoint& endpoint) { - if (this->_socket.connect (endpoint) == -1) + if (_socket.connect (endpoint) == -1) { if (lastError != Errc::TemporaryError) { return nullptr; } - if (!this->_socket.waitConnected (this->_timeout)) + if (!_socket.waitConnected (_timeout)) { - this->_socket.close (); + _socket.close (); return nullptr; } } @@ -153,19 +163,19 @@ namespace join */ BasicSocketStreambuf* disconnect () { - if (this->_socket.connected () && (this->overflow (traits_type::eof ()) == traits_type::eof ())) + if (_socket.connected () && (overflow (traits_type::eof ()) == traits_type::eof ())) { return nullptr; } - if (this->_socket.disconnect () == -1) + if (_socket.disconnect () == -1) { if (lastError != Errc::TemporaryError) { return nullptr; } - if (!this->_socket.waitDisconnected (this->_timeout)) + if (!_socket.waitDisconnected (_timeout)) { return nullptr; } @@ -180,7 +190,7 @@ namespace join */ void close () { - this->_socket.close (); + _socket.close (); } /** @@ -189,7 +199,7 @@ namespace join */ void timeout (int ms) { - this->_timeout = ms; + _timeout = ms; } /** @@ -198,7 +208,7 @@ namespace join */ int timeout () const { - return this->_timeout; + return _timeout; } /** @@ -207,7 +217,7 @@ namespace join */ Socket& socket () { - return this->_socket; + return _socket; } protected: @@ -217,41 +227,41 @@ namespace join */ virtual int_type underflow () override { - if (!this->_socket.connected ()) + if (!_socket.connected ()) { lastError = make_error_code (Errc::ConnectionClosed); return traits_type::eof (); } - if (this->eback () == nullptr) + if (eback () == nullptr) { - this->setg (this->_buf.get (), this->_buf.get (), this->_buf.get ()); + setg (_buf.get (), _buf.get (), _buf.get ()); } - if (this->gptr () == this->egptr ()) + if (gptr () == egptr ()) { for (;;) { - int nread = this->_socket.read (this->eback (), _bufsize); + int nread = _socket.read (eback (), _bufsize); if (nread == -1) { if (lastError == Errc::TemporaryError) { - if (this->_socket.waitReadyRead (this->_timeout)) + if (_socket.waitReadyRead (_timeout)) { continue; } } - this->_socket.close (); + _socket.close (); return traits_type::eof (); } - this->setg (this->eback (), this->eback (), this->eback () + nread); + setg (eback (), eback (), eback () + nread); break; } } - return traits_type::to_int_type (*this->gptr ()); + return traits_type::to_int_type (*gptr ()); } /** @@ -261,30 +271,30 @@ namespace join */ virtual int_type overflow (int_type c = traits_type::eof ()) override { - if (!this->_socket.connected ()) + if (!_socket.connected ()) { lastError = make_error_code (Errc::ConnectionClosed); return traits_type::eof (); } - if (this->pbase () == nullptr) + if (pbase () == nullptr) { - this->setp (this->_buf.get () + _bufsize, this->_buf.get () + (2 * _bufsize)); + setp (_buf.get () + _bufsize, _buf.get () + (2 * _bufsize)); } - if ((this->pptr () == this->epptr ()) || (c == traits_type::eof ())) + if ((pptr () == epptr ()) || (c == traits_type::eof ())) { - std::streamsize pending = this->pptr () - this->pbase (); + std::streamsize pending = pptr () - pbase (); if (pending) { - if (this->_socket.writeExactly (this->pbase (), pending, this->_timeout) == -1) + if (_socket.writeExactly (pbase (), pending, _timeout) == -1) { - this->_socket.close (); + _socket.close (); return traits_type::eof (); } } - this->setp (this->pbase (), this->pbase () + _bufsize); + setp (pbase (), pbase () + _bufsize); if (c == traits_type::eof ()) { @@ -292,7 +302,7 @@ namespace join } } - return this->sputc (traits_type::to_char_type (c)); + return sputc (traits_type::to_char_type (c)); } /** @@ -301,7 +311,7 @@ namespace join */ virtual int_type sync () override { - if (!this->_socket.connected () || (this->overflow () == traits_type::eof ())) + if (!_socket.connected () || (overflow () == traits_type::eof ())) { return -1; } @@ -340,6 +350,16 @@ namespace join { } + /** + * @brief construct the socket stream by moving an existing socket in. + * @param socket socket to move in. + */ + explicit BasicSocketStream (Socket&& socket) + : std::iostream (&_sockbuf) + , _sockbuf (std::move (socket)) + { + } + /** * @brief copy constructor. * @param other other object to copy. @@ -361,7 +381,7 @@ namespace join : std::iostream (std::move (other)) , _sockbuf (std::move (other._sockbuf)) { - this->set_rdbuf (&this->_sockbuf); + set_rdbuf (&_sockbuf); } /** @@ -372,7 +392,7 @@ namespace join BasicSocketStream& operator= (BasicSocketStream&& other) { std::iostream::operator= (std::move (other)); - this->_sockbuf = std::move (other._sockbuf); + _sockbuf = std::move (other._sockbuf); return *this; } @@ -388,9 +408,9 @@ namespace join */ virtual void bind (const Endpoint& endpoint) { - if (this->_sockbuf.bind (endpoint) == nullptr) + if (_sockbuf.bind (endpoint) == nullptr) { - this->setstate (std::ios_base::failbit); + setstate (std::ios_base::failbit); } } @@ -401,9 +421,9 @@ namespace join */ virtual void connect (const Endpoint& endpoint) { - if (this->_sockbuf.connect (endpoint) == nullptr) + if (_sockbuf.connect (endpoint) == nullptr) { - this->setstate (std::ios_base::failbit); + setstate (std::ios_base::failbit); } } @@ -413,9 +433,9 @@ namespace join */ virtual void disconnect () { - if (this->_sockbuf.disconnect () == nullptr) + if (_sockbuf.disconnect () == nullptr) { - this->setstate (std::ios_base::failbit); + setstate (std::ios_base::failbit); } } @@ -425,7 +445,7 @@ namespace join */ virtual void close () { - this->_sockbuf.close (); + _sockbuf.close (); } /** @@ -434,16 +454,16 @@ namespace join */ Endpoint localEndpoint () { - return this->_sockbuf.socket ().localEndpoint (); + return _sockbuf.socket ().localEndpoint (); } /** * @brief determine the remote endpoint associated with this socket. * @return remote endpoint. */ - const Endpoint& remoteEndpoint () + Endpoint remoteEndpoint () { - return this->_sockbuf.socket ().remoteEndpoint (); + return _sockbuf.socket ().remoteEndpoint (); } /** @@ -452,7 +472,7 @@ namespace join */ bool opened () { - return this->_sockbuf.socket ().opened (); + return _sockbuf.socket ().opened (); } /** @@ -461,16 +481,7 @@ namespace join */ bool connected () { - return this->_sockbuf.socket ().connected (); - } - - /** - * @brief check if the socket is secure. - * @return true if the socket is secure, false otherwise. - */ - bool encrypted () - { - return this->_sockbuf.socket ().encrypted (); + return _sockbuf.socket ().connected (); } /** @@ -479,7 +490,7 @@ namespace join */ void timeout (int ms) { - this->_sockbuf.timeout (ms); + _sockbuf.timeout (ms); } /** @@ -488,7 +499,7 @@ namespace join */ int timeout () const { - return this->_sockbuf.timeout (); + return _sockbuf.timeout (); } /** @@ -497,7 +508,7 @@ namespace join */ Socket& socket () { - return this->_sockbuf.socket (); + return _sockbuf.socket (); } protected: diff --git a/core/include/join/stream_socket.hpp b/core/include/join/stream_socket.hpp new file mode 100644 index 00000000..6e329e01 --- /dev/null +++ b/core/include/join/stream_socket.hpp @@ -0,0 +1,493 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_CORE_STREAM_SOCKET_HPP +#define JOIN_CORE_STREAM_SOCKET_HPP + +// libjoin. +#include + +// C++. +#include + +// C. +#include + +namespace join +{ + /** + * @brief basic stream socket class. + */ + template + class BasicStreamSocket final : public BasicSocket + { + /// friendship with basic stream acceptor + friend class BasicStreamAcceptor; + + public: + using Ptr = std::unique_ptr>; + using Mode = typename BasicSocket::Mode; + using Option = typename BasicSocket::Option; + using State = typename BasicSocket::State; + using Endpoint = typename Protocol::Endpoint; + + /** + * @brief default constructor. + */ + BasicStreamSocket () + : BasicStreamSocket (Mode::NonBlocking) + { + } + + /** + * @brief create instance specifying the mode. + * @param mode Set the socket blocking mode. + */ + explicit BasicStreamSocket (Mode mode) + : BasicSocket (mode) + { + } + + /** + * @brief copy constructor. + * @param other other object to copy. + */ + BasicStreamSocket (const BasicStreamSocket& other) = delete; + + /** + * @brief copy assignment operator. + * @param other other object to assign. + * @return assigned object. + */ + BasicStreamSocket& operator= (const BasicStreamSocket& other) = delete; + + /** + * @brief move constructor. + * @param other other object to move. + */ + BasicStreamSocket (BasicStreamSocket&& other) noexcept + : BasicSocket (std::move (other)) + , _remote (std::move (other._remote)) + { + } + + /** + * @brief move assignment operator. + * @param other other object to assign. + * @return assigned object. + */ + BasicStreamSocket& operator= (BasicStreamSocket&& other) noexcept + { + BasicSocket::operator= (std::move (other)); + + _remote = std::move (other._remote); + + return *this; + } + + /** + * @brief destroy the instance. + */ + ~BasicStreamSocket () = default; + + /** + * @brief make a connection to the given endpoint. + * @param endpoint endpoint to connect to. + * @return 0 on success, -1 on failure. + */ + int connect (const Endpoint& endpoint) noexcept + { + if ((this->_state != State::Closed) && (this->_state != State::Disconnected)) + { + lastError = make_error_code (Errc::InUse); + return -1; + } + + if ((this->_state == State::Closed) && (this->open (endpoint.protocol ()) == -1)) + { + return -1; + } + + int result = ::connect (this->_handle, endpoint.addr (), endpoint.length ()); + + this->_state = State::Connecting; + _remote = endpoint; + + if (result == -1) + { + lastError = std::error_code (errno, std::generic_category ()); + if (lastError != std::errc::operation_in_progress) + { + close (); + } + return -1; + } + + this->_state = State::Connected; + + return 0; + } + + /** + * @brief block until connected. + * @param timeout timeout in milliseconds. + * @return true if connected, false otherwise. + */ + bool waitConnected (int timeout = 0) + { + if (this->_state != State::Connected) + { + if (this->_state != State::Connecting) + { + lastError = make_error_code (Errc::OperationFailed); + return false; + } + + if (!this->waitReadyWrite (timeout)) + { + return false; + } + + return connected (); + } + + return true; + } + + /** + * @brief shutdown the connection. + * @return 0 on success, -1 on failure. + */ + int disconnect () noexcept + { + if (this->_state == State::Connected) + { + ::shutdown (this->_handle, SHUT_WR); + this->_state = State::Disconnecting; + } + + if (this->_state == State::Disconnecting) + { + char buffer[4096]; + // closing before reading can make the client + // not see all of our output. + // we have to do a "lingering close" + for (;;) + { + int result = this->read (buffer, sizeof (buffer)); + if (result <= 0) + { + if ((result == -1) && (lastError == Errc::TemporaryError)) + { + return -1; + } + + break; + } + } + + ::shutdown (this->_handle, SHUT_RD); + this->_state = State::Disconnected; + } + + return 0; + } + + /** + * @brief wait until the connection as been shut down. + * @param timeout timeout in milliseconds. + * return true if the connection as been shut down, false otherwise. + */ + bool waitDisconnected (int timeout = 0) + { + if ((this->_state != State::Disconnected) && (this->_state != State::Closed)) + { + if (this->_state != State::Disconnecting) + { + lastError = make_error_code (Errc::OperationFailed); + return false; + } + + auto start = std::chrono::steady_clock::now (); + int elapsed = 0; + + while ((lastError == Errc::TemporaryError) && (elapsed <= timeout)) + { + if (!this->waitReadyRead (timeout - elapsed)) + { + return false; + } + + if (disconnect () == 0) + { + return true; + } + + if (timeout) + { + elapsed = std::chrono::duration_cast ( + std::chrono::steady_clock::now () - start) + .count (); + } + } + + return false; + } + + return true; + } + + /** + * @brief close the socket handle. + */ + void close () noexcept override + { + BasicSocket::close (); + _remote = {}; + } + + /** + * @brief read data until size is reached or an error occurred. + * @param data buffer used to store the data received. + * @param size number of bytes to read. + * @param timeout timeout in milliseconds. + * @return 0 on success, -1 on failure. + */ + int readExactly (char* data, unsigned long size, int timeout = 0) noexcept + { + unsigned long numRead = 0; + + while (numRead < size) + { + int result = this->read (data + numRead, size - numRead); + if (result == -1) + { + if (lastError == Errc::TemporaryError) + { + if (this->waitReadyRead (timeout)) + { + continue; + } + } + + return -1; + } + + numRead += result; + } + + return 0; + } + + /** + * @brief write data until size is reached or an error occurred. + * @param data data buffer to send. + * @param size number of bytes to write. + * @param timeout timeout in milliseconds. + * @return 0 on success, -1 on failure. + */ + int writeExactly (const char* data, unsigned long size, int timeout = 0) noexcept + { + unsigned long numWrite = 0; + + while (numWrite < size) + { + int result = this->write (data + numWrite, size - numWrite); + if (result == -1) + { + if (lastError == Errc::TemporaryError) + { + if (this->waitReadyWrite (timeout)) + { + continue; + } + } + + return -1; + } + + numWrite += result; + } + + return 0; + } + + /** + * @brief set the given option to the given value. + * @param option socket option. + * @param value option value. + * @return 0 on success, -1 on failure. + */ + int setOption (Option option, int value) noexcept override + { + if (this->_state == State::Closed) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + int optlevel, optname; + + switch (option) + { + case Option::NoDelay: + optlevel = IPPROTO_TCP; + optname = TCP_NODELAY; + break; + + case Option::KeepIdle: + optlevel = IPPROTO_TCP; + optname = TCP_KEEPIDLE; + break; + + case Option::KeepIntvl: + optlevel = IPPROTO_TCP; + optname = TCP_KEEPINTVL; + break; + + case Option::KeepCount: + optlevel = IPPROTO_TCP; + optname = TCP_KEEPCNT; + break; + + default: + return BasicSocket::setOption (option, value); + } + + int result = ::setsockopt (this->_handle, optlevel, optname, &value, sizeof (value)); + if (result == -1) + { + lastError = std::error_code (errno, std::generic_category ()); + return -1; + } + + return 0; + } + + /** + * @brief determine the remote endpoint associated with this socket. + * @return remote endpoint. + */ + const Endpoint& remoteEndpoint () const noexcept + { + return _remote; + } + + /** + * @brief check if the socket is connecting. + * @return true if connecting, false otherwise. + */ + bool connecting () const noexcept + { + return (this->_state == State::Connecting); + } + + /** + * @brief check if the socket is connected. + * @return true if connected, false otherwise. + * @note this method probes SO_ERROR and updates the internal state when + * a pending connection completes, it is therefore not const. + */ + bool connected () noexcept + { + if (this->_state == State::Connected) + { + return true; + } + else if (this->_state != State::Connecting) + { + return false; + } + + int optval; + socklen_t optlen = sizeof (optval); + + int result = ::getsockopt (this->_handle, SOL_SOCKET, SO_ERROR, &optval, &optlen); + if ((result == -1) || (optval != 0)) + { + return false; + } + + this->_state = State::Connected; + + return true; + } + + /** + * @brief get socket mtu. + * @return mtu on success, -1 on failure. + */ + int mtu () const noexcept + { + if (this->_state == State::Closed) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + int result = -1, value = -1; + socklen_t valueLen = sizeof (value); + + if (this->_protocol.family () == AF_INET6) + { + result = ::getsockopt (this->_handle, IPPROTO_IPV6, IPV6_MTU, &value, &valueLen); + } + else if (this->_protocol.family () == AF_INET) + { + result = ::getsockopt (this->_handle, IPPROTO_IP, IP_MTU, &value, &valueLen); + } + else + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + if (result == -1) + { + lastError = std::error_code (errno, std::generic_category ()); + return -1; + } + + return value; + } + + protected: + /// remote endpoint. + Endpoint _remote; + }; + + /** + * @brief compare if socket handle is inferior. + * @param a socket handle to compare. + * @param b socket handle to compare to. + * @return true if inferior. + */ + template + inline bool operator< (const BasicStreamSocket& a, const BasicStreamSocket& b) noexcept + { + return a.handle () < b.handle (); + } +} + +#endif diff --git a/core/include/join/threadpool.hpp b/core/include/join/thread_pool.hpp similarity index 99% rename from core/include/join/threadpool.hpp rename to core/include/join/thread_pool.hpp index 4fd1f800..dc6ee514 100644 --- a/core/include/join/threadpool.hpp +++ b/core/include/join/thread_pool.hpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#ifndef JOIN_CORE_THREADPOOL_HPP -#define JOIN_CORE_THREADPOOL_HPP +#ifndef JOIN_CORE_THREAD_POOL_HPP +#define JOIN_CORE_THREAD_POOL_HPP // libjoin. #include diff --git a/core/src/ipaddress.cpp b/core/src/ip_address.cpp similarity index 99% rename from core/src/ipaddress.cpp rename to core/src/ip_address.cpp index a5d6519d..81377416 100644 --- a/core/src/ipaddress.cpp +++ b/core/src/ip_address.cpp @@ -23,20 +23,20 @@ */ // libjoin. +#include #include -#include // C++. -#include -#include -#include #include +#include +#include +#include // C. -#include -#include -#include #include +#include +#include +#include namespace join { diff --git a/core/src/macaddress.cpp b/core/src/mac_address.cpp similarity index 99% rename from core/src/macaddress.cpp rename to core/src/mac_address.cpp index b52c5c77..c6d3c345 100644 --- a/core/src/macaddress.cpp +++ b/core/src/mac_address.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include // C++. diff --git a/core/src/threadpool.cpp b/core/src/thread_pool.cpp similarity index 99% rename from core/src/threadpool.cpp rename to core/src/thread_pool.cpp index 4cc6575d..a2fb9a1e 100644 --- a/core/src/threadpool.cpp +++ b/core/src/thread_pool.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include using join::WorkerThread; using join::ThreadPool; diff --git a/core/tests/CMakeLists.txt b/core/tests/CMakeLists.txt index 0411efce..9d126d41 100644 --- a/core/tests/CMakeLists.txt +++ b/core/tests/CMakeLists.txt @@ -61,125 +61,125 @@ if(JOIN_ENABLE_IO_URING) install(TARGETS sqpoll_proactor.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) endif() -add_executable(realtimetimer.gtest realtimetimer_test.cpp) -target_link_libraries(realtimetimer.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME realtimetimer.gtest COMMAND realtimetimer.gtest) -install(TARGETS realtimetimer.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(monotonictimer.gtest monotonictimer_test.cpp) -target_link_libraries(monotonictimer.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME monotonictimer.gtest COMMAND monotonictimer.gtest) -install(TARGETS monotonictimer.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(monotonicstats.gtest monotonicstats_test.cpp) -target_link_libraries(monotonicstats.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME monotonicstats.gtest COMMAND monotonicstats.gtest) -install(TARGETS monotonicstats.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(monotonicrawstats.gtest monotonicrawstats_test.cpp) -target_link_libraries(monotonicrawstats.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME monotonicrawstats.gtest COMMAND monotonicrawstats.gtest) -install(TARGETS monotonicrawstats.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(rdtscstats.gtest rdtscstats_test.cpp) -target_link_libraries(rdtscstats.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME rdtscstats.gtest COMMAND rdtscstats.gtest) -install(TARGETS rdtscstats.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(localmem.gtest localmem_test.cpp) -target_link_libraries(localmem.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME localmem.gtest COMMAND localmem.gtest) -install(TARGETS localmem.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(localalloc.gtest localalloc_test.cpp) -target_link_libraries(localalloc.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME localalloc.gtest COMMAND localalloc.gtest) -install(TARGETS localalloc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(localspsc.gtest localspsc_test.cpp) -target_link_libraries(localspsc.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME localspsc.gtest COMMAND localspsc.gtest) -install(TARGETS localspsc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(localmpsc.gtest localmpsc_test.cpp) -target_link_libraries(localmpsc.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME localmpsc.gtest COMMAND localmpsc.gtest) -install(TARGETS localmpsc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(localmpmc.gtest localmpmc_test.cpp) -target_link_libraries(localmpmc.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME localmpmc.gtest COMMAND localmpmc.gtest) -install(TARGETS localmpmc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(shmmem.gtest shmmem_test.cpp) -target_link_libraries(shmmem.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME shmmem.gtest COMMAND shmmem.gtest) -install(TARGETS shmmem.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(shmalloc.gtest shmalloc_test.cpp) -target_link_libraries(shmalloc.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME shmalloc.gtest COMMAND shmalloc.gtest) -install(TARGETS shmalloc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(shmspsc.gtest shmspsc_test.cpp) -target_link_libraries(shmspsc.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME shmspsc.gtest COMMAND shmspsc.gtest) -install(TARGETS shmspsc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(shmmpsc.gtest shmmpsc_test.cpp) -target_link_libraries(shmmpsc.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME shmmpsc.gtest COMMAND shmmpsc.gtest) -install(TARGETS shmmpsc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(shmmpmc.gtest shmmpmc_test.cpp) -target_link_libraries(shmmpmc.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME shmmpmc.gtest COMMAND shmmpmc.gtest) -install(TARGETS shmmpmc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(realtime_timer.gtest realtime_timer_test.cpp) +target_link_libraries(realtime_timer.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME realtime_timer.gtest COMMAND realtime_timer.gtest) +install(TARGETS realtime_timer.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(monotonic_timer.gtest monotonic_timer_test.cpp) +target_link_libraries(monotonic_timer.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME monotonic_timer.gtest COMMAND monotonic_timer.gtest) +install(TARGETS monotonic_timer.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(monotonic_stats.gtest monotonic_stats_test.cpp) +target_link_libraries(monotonic_stats.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME monotonic_stats.gtest COMMAND monotonic_stats.gtest) +install(TARGETS monotonic_stats.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(monotonicraw_stats.gtest monotonicraw_stats_test.cpp) +target_link_libraries(monotonicraw_stats.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME monotonicraw_stats.gtest COMMAND monotonicraw_stats.gtest) +install(TARGETS monotonicraw_stats.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(rdtsc_stats.gtest rdtsc_stats_test.cpp) +target_link_libraries(rdtsc_stats.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME rdtsc_stats.gtest COMMAND rdtsc_stats.gtest) +install(TARGETS rdtsc_stats.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(local_mem.gtest local_mem_test.cpp) +target_link_libraries(local_mem.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME local_mem.gtest COMMAND local_mem.gtest) +install(TARGETS local_mem.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(local_alloc.gtest local_alloc_test.cpp) +target_link_libraries(local_alloc.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME local_alloc.gtest COMMAND local_alloc.gtest) +install(TARGETS local_alloc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(local_spsc.gtest local_spsc_test.cpp) +target_link_libraries(local_spsc.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME local_spsc.gtest COMMAND local_spsc.gtest) +install(TARGETS local_spsc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(local_mpsc.gtest local_mpsc_test.cpp) +target_link_libraries(local_mpsc.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME local_mpsc.gtest COMMAND local_mpsc.gtest) +install(TARGETS local_mpsc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(local_mpmc.gtest local_mpmc_test.cpp) +target_link_libraries(local_mpmc.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME local_mpmc.gtest COMMAND local_mpmc.gtest) +install(TARGETS local_mpmc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(shm_mem.gtest shm_mem_test.cpp) +target_link_libraries(shm_mem.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME shm_mem.gtest COMMAND shm_mem.gtest) +install(TARGETS shm_mem.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(shm_alloc.gtest shm_alloc_test.cpp) +target_link_libraries(shm_alloc.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME shm_alloc.gtest COMMAND shm_alloc.gtest) +install(TARGETS shm_alloc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(shm_spsc.gtest shm_spsc_test.cpp) +target_link_libraries(shm_spsc.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME shm_spsc.gtest COMMAND shm_spsc.gtest) +install(TARGETS shm_spsc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(shm_mpsc.gtest shm_mpsc_test.cpp) +target_link_libraries(shm_mpsc.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME shm_mpsc.gtest COMMAND shm_mpsc.gtest) +install(TARGETS shm_mpsc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(shm_mpmc.gtest shm_mpmc_test.cpp) +target_link_libraries(shm_mpmc.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME shm_mpmc.gtest COMMAND shm_mpmc.gtest) +install(TARGETS shm_mpmc.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(mutex.gtest mutex_test.cpp) target_link_libraries(mutex.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME mutex.gtest COMMAND mutex.gtest) install(TARGETS mutex.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(recursivemutex.gtest recursivemutex_test.cpp) -target_link_libraries(recursivemutex.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME recursivemutex.gtest COMMAND recursivemutex.gtest) -install(TARGETS recursivemutex.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(recursive_mutex.gtest recursive_mutex_test.cpp) +target_link_libraries(recursive_mutex.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME recursive_mutex.gtest COMMAND recursive_mutex.gtest) +install(TARGETS recursive_mutex.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(sharedmutex.gtest sharedmutex_test.cpp) -target_link_libraries(sharedmutex.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME sharedmutex.gtest COMMAND sharedmutex.gtest) -install(TARGETS sharedmutex.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(shared_mutex.gtest shared_mutex_test.cpp) +target_link_libraries(shared_mutex.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME shared_mutex.gtest COMMAND shared_mutex.gtest) +install(TARGETS shared_mutex.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(condition.gtest condition_test.cpp) target_link_libraries(condition.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME condition.gtest COMMAND condition.gtest) install(TARGETS condition.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(sharedcondition.gtest sharedcondition_test.cpp) -target_link_libraries(sharedcondition.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME sharedcondition.gtest COMMAND sharedcondition.gtest) -install(TARGETS sharedcondition.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(shared_condition.gtest shared_condition_test.cpp) +target_link_libraries(shared_condition.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME shared_condition.gtest COMMAND shared_condition.gtest) +install(TARGETS shared_condition.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(semaphore.gtest semaphore_test.cpp) target_link_libraries(semaphore.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME semaphore.gtest COMMAND semaphore.gtest) install(TARGETS semaphore.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(sharedsemaphore.gtest sharedsemaphore_test.cpp) -target_link_libraries(sharedsemaphore.gtest ${JOIN_CORE} GTest::gtest_main rt) -add_test(NAME sharedsemaphore.gtest COMMAND sharedsemaphore.gtest) -install(TARGETS sharedsemaphore.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(shared_semaphore.gtest shared_semaphore_test.cpp) +target_link_libraries(shared_semaphore.gtest ${JOIN_CORE} GTest::gtest_main rt) +add_test(NAME shared_semaphore.gtest COMMAND shared_semaphore.gtest) +install(TARGETS shared_semaphore.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(futex.gtest futex_test.cpp) target_link_libraries(futex.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME futex.gtest COMMAND futex.gtest) install(TARGETS futex.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(sharedfutex.gtest sharedfutex_test.cpp) -target_link_libraries(sharedfutex.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME sharedfutex.gtest COMMAND sharedfutex.gtest) -install(TARGETS sharedfutex.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(shared_futex.gtest shared_futex_test.cpp) +target_link_libraries(shared_futex.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME shared_futex.gtest COMMAND shared_futex.gtest) +install(TARGETS shared_futex.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(function.gtest function_test.cpp) target_link_libraries(function.gtest ${JOIN_CORE} GTest::gtest_main) @@ -191,20 +191,20 @@ target_link_libraries(thread.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME thread.gtest COMMAND thread.gtest) install(TARGETS thread.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(threadpool.gtest threadpool_test.cpp) -target_link_libraries(threadpool.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME threadpool.gtest COMMAND threadpool.gtest) -install(TARGETS threadpool.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(thread_pool.gtest thread_pool_test.cpp) +target_link_libraries(thread_pool.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME thread_pool.gtest COMMAND thread_pool.gtest) +install(TARGETS thread_pool.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(macaddress.gtest macaddress_test.cpp) -target_link_libraries(macaddress.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME macaddress.gtest COMMAND macaddress.gtest) -install(TARGETS macaddress.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(mac_address.gtest mac_address_test.cpp) +target_link_libraries(mac_address.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME mac_address.gtest COMMAND mac_address.gtest) +install(TARGETS mac_address.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(ipaddress.gtest ipaddress_test.cpp) -target_link_libraries(ipaddress.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME ipaddress.gtest COMMAND ipaddress.gtest) -install(TARGETS ipaddress.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(ip_address.gtest ip_address_test.cpp) +target_link_libraries(ip_address.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME ip_address.gtest COMMAND ip_address.gtest) +install(TARGETS ip_address.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(endpoint.gtest endpoint_test.cpp) target_link_libraries(endpoint.gtest ${JOIN_CORE} GTest::gtest_main) @@ -216,50 +216,50 @@ target_link_libraries(protocol.gtest ${JOIN_CORE} GTest::gtest_main) add_test(NAME protocol.gtest COMMAND protocol.gtest) install(TARGETS protocol.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(unixdgramsocket.gtest unixdgramsocket_test.cpp) -target_link_libraries(unixdgramsocket.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME unixdgramsocket.gtest COMMAND unixdgramsocket.gtest) -install(TARGETS unixdgramsocket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(unixstreamsocket.gtest unixstreamsocket_test.cpp) -target_link_libraries(unixstreamsocket.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME unixstreamsocket.gtest COMMAND unixstreamsocket.gtest) -install(TARGETS unixstreamsocket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(unixstreamacceptor.gtest unixstreamacceptor_test.cpp) -target_link_libraries(unixstreamacceptor.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME unixstreamacceptor.gtest COMMAND unixstreamacceptor.gtest) -install(TARGETS unixstreamacceptor.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(rawsocket.gtest rawsocket_test.cpp) -target_link_libraries(rawsocket.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME rawsocket.gtest COMMAND rawsocket.gtest) -install(TARGETS rawsocket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(udpsocket.gtest udpsocket_test.cpp) -target_link_libraries(udpsocket.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME udpsocket.gtest COMMAND udpsocket.gtest) -install(TARGETS udpsocket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(icmpsocket.gtest icmpsocket_test.cpp) -target_link_libraries(icmpsocket.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME icmpsocket.gtest COMMAND icmpsocket.gtest) -install(TARGETS icmpsocket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(tcpsocket.gtest tcpsocket_test.cpp) -target_link_libraries(tcpsocket.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME tcpsocket.gtest COMMAND tcpsocket.gtest) -install(TARGETS tcpsocket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(tcpsocketstream.gtest tcpsocketstream_test.cpp) -target_link_libraries(tcpsocketstream.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME tcpsocketstream.gtest COMMAND tcpsocketstream.gtest) -install(TARGETS tcpsocketstream.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(tcpacceptor.gtest tcpacceptor_test.cpp) -target_link_libraries(tcpacceptor.gtest ${JOIN_CORE} GTest::gtest_main) -add_test(NAME tcpacceptor.gtest COMMAND tcpacceptor.gtest) -install(TARGETS tcpacceptor.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(unix_datagram_socket.gtest unix_datagram_socket_test.cpp) +target_link_libraries(unix_datagram_socket.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME unix_datagram_socket.gtest COMMAND unix_datagram_socket.gtest) +install(TARGETS unix_datagram_socket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(unix_stream_socket.gtest unix_stream_socket_test.cpp) +target_link_libraries(unix_stream_socket.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME unix_stream_socket.gtest COMMAND unix_stream_socket.gtest) +install(TARGETS unix_stream_socket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(unix_stream_acceptor.gtest unix_stream_acceptor_test.cpp) +target_link_libraries(unix_stream_acceptor.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME unix_stream_acceptor.gtest COMMAND unix_stream_acceptor.gtest) +install(TARGETS unix_stream_acceptor.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(raw_socket.gtest raw_socket_test.cpp) +target_link_libraries(raw_socket.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME raw_socket.gtest COMMAND raw_socket.gtest) +install(TARGETS raw_socket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(udp_socket.gtest udp_socket_test.cpp) +target_link_libraries(udp_socket.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME udp_socket.gtest COMMAND udp_socket.gtest) +install(TARGETS udp_socket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(icmp_socket.gtest icmp_socket_test.cpp) +target_link_libraries(icmp_socket.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME icmp_socket.gtest COMMAND icmp_socket.gtest) +install(TARGETS icmp_socket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(tcp_socket.gtest tcp_socket_test.cpp) +target_link_libraries(tcp_socket.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME tcp_socket.gtest COMMAND tcp_socket.gtest) +install(TARGETS tcp_socket.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(tcp_socket_stream.gtest tcp_socket_stream_test.cpp) +target_link_libraries(tcp_socket_stream.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME tcp_socket_stream.gtest COMMAND tcp_socket_stream.gtest) +install(TARGETS tcp_socket_stream.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(tcp_acceptor.gtest tcp_acceptor_test.cpp) +target_link_libraries(tcp_acceptor.gtest ${JOIN_CORE} GTest::gtest_main) +add_test(NAME tcp_acceptor.gtest COMMAND tcp_acceptor.gtest) +install(TARGETS tcp_acceptor.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(cpu.gtest cpu_test.cpp) target_link_libraries(cpu.gtest ${JOIN_CORE} GTest::gtest_main) diff --git a/core/tests/icmpsocket_test.cpp b/core/tests/icmp_socket_test.cpp similarity index 98% rename from core/tests/icmpsocket_test.cpp rename to core/tests/icmp_socket_test.cpp index f64fac62..b9da1233 100644 --- a/core/tests/icmpsocket_test.cpp +++ b/core/tests/icmp_socket_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include @@ -469,20 +469,6 @@ TEST_F (IcmpSocket, connected) ASSERT_FALSE (icmpSocket.connected ()); } -/** - * @brief Test encrypted method. - */ -TEST_F (IcmpSocket, encrypted) -{ - Icmp::Socket icmpSocket (Icmp::Socket::Blocking); - - ASSERT_FALSE (icmpSocket.encrypted ()); - ASSERT_EQ (icmpSocket.connect (_host), 0) << join::lastError.message (); - ASSERT_FALSE (icmpSocket.encrypted ()); - icmpSocket.close (); - ASSERT_FALSE (icmpSocket.encrypted ()); -} - /** * @brief Test family method. */ diff --git a/core/tests/ipaddress_test.cpp b/core/tests/ip_address_test.cpp similarity index 99% rename from core/tests/ipaddress_test.cpp rename to core/tests/ip_address_test.cpp index b8091bc5..3136f0c3 100644 --- a/core/tests/ipaddress_test.cpp +++ b/core/tests/ip_address_test.cpp @@ -23,8 +23,8 @@ */ // libjoin. +#include #include -#include // Libraries. #include diff --git a/core/tests/localalloc_test.cpp b/core/tests/local_alloc_test.cpp similarity index 100% rename from core/tests/localalloc_test.cpp rename to core/tests/local_alloc_test.cpp diff --git a/core/tests/localmem_test.cpp b/core/tests/local_mem_test.cpp similarity index 100% rename from core/tests/localmem_test.cpp rename to core/tests/local_mem_test.cpp diff --git a/core/tests/localmpmc_test.cpp b/core/tests/local_mpmc_test.cpp similarity index 100% rename from core/tests/localmpmc_test.cpp rename to core/tests/local_mpmc_test.cpp diff --git a/core/tests/localmpsc_test.cpp b/core/tests/local_mpsc_test.cpp similarity index 100% rename from core/tests/localmpsc_test.cpp rename to core/tests/local_mpsc_test.cpp diff --git a/core/tests/localspsc_test.cpp b/core/tests/local_spsc_test.cpp similarity index 100% rename from core/tests/localspsc_test.cpp rename to core/tests/local_spsc_test.cpp diff --git a/core/tests/macaddress_test.cpp b/core/tests/mac_address_test.cpp similarity index 99% rename from core/tests/macaddress_test.cpp rename to core/tests/mac_address_test.cpp index 1d95c845..30d63da5 100644 --- a/core/tests/macaddress_test.cpp +++ b/core/tests/mac_address_test.cpp @@ -23,8 +23,8 @@ */ // libjoin. +#include #include -#include // Libraries. #include diff --git a/core/tests/monotonicstats_test.cpp b/core/tests/monotonic_stats_test.cpp similarity index 100% rename from core/tests/monotonicstats_test.cpp rename to core/tests/monotonic_stats_test.cpp diff --git a/core/tests/monotonictimer_test.cpp b/core/tests/monotonic_timer_test.cpp similarity index 100% rename from core/tests/monotonictimer_test.cpp rename to core/tests/monotonic_timer_test.cpp diff --git a/core/tests/monotonicrawstats_test.cpp b/core/tests/monotonicraw_stats_test.cpp similarity index 100% rename from core/tests/monotonicrawstats_test.cpp rename to core/tests/monotonicraw_stats_test.cpp diff --git a/core/tests/rawsocket_test.cpp b/core/tests/raw_socket_test.cpp similarity index 97% rename from core/tests/rawsocket_test.cpp rename to core/tests/raw_socket_test.cpp index e5949216..851a8950 100644 --- a/core/tests/rawsocket_test.cpp +++ b/core/tests/raw_socket_test.cpp @@ -23,8 +23,8 @@ */ // libjoin. +#include #include -#include // Libraries. #include @@ -364,20 +364,6 @@ TEST_F (RawSocket, opened) ASSERT_FALSE (rawSocket.opened ()); } -/** - * @brief Test encrypted method. - */ -TEST_F (RawSocket, encrypted) -{ - Raw::Socket rawSocket; - - ASSERT_FALSE (rawSocket.encrypted ()); - ASSERT_EQ (rawSocket.open (), 0) << join::lastError.message (); - ASSERT_FALSE (rawSocket.encrypted ()); - rawSocket.close (); - ASSERT_FALSE (rawSocket.encrypted ()); -} - /** * @brief Test family method. */ diff --git a/core/tests/rdtscstats_test.cpp b/core/tests/rdtsc_stats_test.cpp similarity index 100% rename from core/tests/rdtscstats_test.cpp rename to core/tests/rdtsc_stats_test.cpp diff --git a/core/tests/reactor_test.cpp b/core/tests/reactor_test.cpp index c68a9af2..477b0ba3 100644 --- a/core/tests/reactor_test.cpp +++ b/core/tests/reactor_test.cpp @@ -71,7 +71,8 @@ class ReactorTest : public join::EventHandler, public ::testing::Test { { ScopedLock lock (_mut); - _server.readExactly (_event, _server.canRead ()); + _event.resize (_server.canRead ()); + _server.readExactly (&_event[0], _event.size ()); EventHandler::onReadable (fd); } diff --git a/core/tests/realtimetimer_test.cpp b/core/tests/realtime_timer_test.cpp similarity index 100% rename from core/tests/realtimetimer_test.cpp rename to core/tests/realtime_timer_test.cpp diff --git a/core/tests/recursivemutex_test.cpp b/core/tests/recursive_mutex_test.cpp similarity index 100% rename from core/tests/recursivemutex_test.cpp rename to core/tests/recursive_mutex_test.cpp diff --git a/core/tests/sharedcondition_test.cpp b/core/tests/shared_condition_test.cpp similarity index 100% rename from core/tests/sharedcondition_test.cpp rename to core/tests/shared_condition_test.cpp diff --git a/core/tests/sharedfutex_test.cpp b/core/tests/shared_futex_test.cpp similarity index 100% rename from core/tests/sharedfutex_test.cpp rename to core/tests/shared_futex_test.cpp diff --git a/core/tests/sharedmutex_test.cpp b/core/tests/shared_mutex_test.cpp similarity index 100% rename from core/tests/sharedmutex_test.cpp rename to core/tests/shared_mutex_test.cpp diff --git a/core/tests/sharedsemaphore_test.cpp b/core/tests/shared_semaphore_test.cpp similarity index 100% rename from core/tests/sharedsemaphore_test.cpp rename to core/tests/shared_semaphore_test.cpp diff --git a/core/tests/shmalloc_test.cpp b/core/tests/shm_alloc_test.cpp similarity index 100% rename from core/tests/shmalloc_test.cpp rename to core/tests/shm_alloc_test.cpp diff --git a/core/tests/shmmem_test.cpp b/core/tests/shm_mem_test.cpp similarity index 100% rename from core/tests/shmmem_test.cpp rename to core/tests/shm_mem_test.cpp diff --git a/core/tests/shmmpmc_test.cpp b/core/tests/shm_mpmc_test.cpp similarity index 100% rename from core/tests/shmmpmc_test.cpp rename to core/tests/shm_mpmc_test.cpp diff --git a/core/tests/shmmpsc_test.cpp b/core/tests/shm_mpsc_test.cpp similarity index 100% rename from core/tests/shmmpsc_test.cpp rename to core/tests/shm_mpsc_test.cpp diff --git a/core/tests/shmspsc_test.cpp b/core/tests/shm_spsc_test.cpp similarity index 100% rename from core/tests/shmspsc_test.cpp rename to core/tests/shm_spsc_test.cpp diff --git a/core/tests/tcpacceptor_test.cpp b/core/tests/tcp_acceptor_test.cpp similarity index 100% rename from core/tests/tcpacceptor_test.cpp rename to core/tests/tcp_acceptor_test.cpp diff --git a/core/tests/tcpsocketstream_test.cpp b/core/tests/tcp_socket_stream_test.cpp similarity index 93% rename from core/tests/tcpsocketstream_test.cpp rename to core/tests/tcp_socket_stream_test.cpp index acccfdfa..4af0caf3 100644 --- a/core/tests/tcpsocketstream_test.cpp +++ b/core/tests/tcp_socket_stream_test.cpp @@ -23,8 +23,9 @@ */ // libjoin. -#include +#include #include +#include // Libraries. #include @@ -38,7 +39,7 @@ using join::Tcp; /** * @brief Class used to test the TCP socket stream API. */ -class TcpSocketStream : public Tcp::Acceptor, public EventHandler, public ::testing::Test +class TcpSocketStream : public EventHandler, public ::testing::Test { protected: /** @@ -46,8 +47,8 @@ class TcpSocketStream : public Tcp::Acceptor, public EventHandler, public ::test */ void SetUp () override { - ASSERT_EQ (this->create ({IpAddress::ipv6Wildcard, _port}), 0) << join::lastError.message (); - ASSERT_EQ (ReactorThread::reactor ().addHandler (handle (), this), 0) << join::lastError.message (); + ASSERT_EQ (_server.create ({IpAddress::ipv6Wildcard, _port}), 0) << join::lastError.message (); + ASSERT_EQ (ReactorThread::reactor ().addHandler (_server.handle (), this), 0) << join::lastError.message (); } /** @@ -55,8 +56,8 @@ class TcpSocketStream : public Tcp::Acceptor, public EventHandler, public ::test */ void TearDown () override { - ASSERT_EQ (ReactorThread::reactor ().delHandler (handle ()), 0) << join::lastError.message (); - this->close (); + ASSERT_EQ (ReactorThread::reactor ().delHandler (_server.handle ()), 0) << join::lastError.message (); + _server.close (); } /** @@ -65,7 +66,7 @@ class TcpSocketStream : public Tcp::Acceptor, public EventHandler, public ::test */ virtual void onReadable ([[maybe_unused]] int fd) override { - Tcp::Socket sock = this->accept (); + Tcp::Socket sock = _server.accept (); if (sock.connected ()) { char buf[1024]; @@ -88,6 +89,9 @@ class TcpSocketStream : public Tcp::Acceptor, public EventHandler, public ::test } } + /// server. + Tcp::Acceptor _server; + /// timeout. static const int _timeout; @@ -267,23 +271,6 @@ TEST_F (TcpSocketStream, connected) ASSERT_FALSE (tcpStream.connected ()); } -/** - * @brief Test encrypted method. - */ -TEST_F (TcpSocketStream, encrypted) -{ - Tcp::Stream tcpStream; - ASSERT_FALSE (tcpStream.encrypted ()); - tcpStream.connect ({_host, _port}); - ASSERT_TRUE (tcpStream.good ()) << join::lastError.message (); - ASSERT_TRUE (tcpStream.connected ()); - ASSERT_FALSE (tcpStream.encrypted ()); - tcpStream.close (); - ASSERT_TRUE (tcpStream.good ()) << join::lastError.message (); - ASSERT_FALSE (tcpStream.connected ()); - ASSERT_FALSE (tcpStream.encrypted ()); -} - /** * @brief Test timeout method. */ diff --git a/core/tests/tcpsocket_test.cpp b/core/tests/tcp_socket_test.cpp similarity index 93% rename from core/tests/tcpsocket_test.cpp rename to core/tests/tcp_socket_test.cpp index b3c541ce..b083a31a 100644 --- a/core/tests/tcpsocket_test.cpp +++ b/core/tests/tcp_socket_test.cpp @@ -23,8 +23,8 @@ */ // libjoin. -#include #include +#include // Libraries. #include @@ -38,7 +38,7 @@ using join::Tcp; /** * @brief Class used to test the TCP socket API. */ -class TcpSocket : public Tcp::Acceptor, public EventHandler, public ::testing::Test +class TcpSocket : public EventHandler, public ::testing::Test { protected: /** @@ -46,8 +46,8 @@ class TcpSocket : public Tcp::Acceptor, public EventHandler, public ::testing::T */ void SetUp () override { - ASSERT_EQ (this->create ({IpAddress::ipv6Wildcard, _port}), 0) << join::lastError.message (); - ASSERT_EQ (ReactorThread::reactor ().addHandler (handle (), this), 0) << join::lastError.message (); + ASSERT_EQ (_server.create ({IpAddress::ipv6Wildcard, _port}), 0) << join::lastError.message (); + ASSERT_EQ (ReactorThread::reactor ().addHandler (_server.handle (), this), 0) << join::lastError.message (); } /** @@ -55,8 +55,8 @@ class TcpSocket : public Tcp::Acceptor, public EventHandler, public ::testing::T */ void TearDown () override { - ASSERT_EQ (ReactorThread::reactor ().delHandler (handle ()), 0) << join::lastError.message (); - this->close (); + ASSERT_EQ (ReactorThread::reactor ().delHandler (_server.handle ()), 0) << join::lastError.message (); + _server.close (); } /** @@ -65,7 +65,7 @@ class TcpSocket : public Tcp::Acceptor, public EventHandler, public ::testing::T */ virtual void onReadable ([[maybe_unused]] int fd) override { - Tcp::Socket sock = this->accept (); + Tcp::Socket sock = _server.accept (); if (sock.connected ()) { char buf[1024]; @@ -88,6 +88,9 @@ class TcpSocket : public Tcp::Acceptor, public EventHandler, public ::testing::T } } + /// server. + Tcp::Acceptor _server; + /// host. static const std::string _hostv4; static const std::string _hostv6; @@ -522,7 +525,7 @@ TEST_F (TcpSocket, opened) Tcp::Socket tcpSocket (Tcp::Socket::Blocking); ASSERT_FALSE (tcpSocket.opened ()); - ASSERT_EQ (tcpSocket.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tcpSocket.open (Tcp{IpAddress (_hostv4).family ()}), 0) << join::lastError.message (); ASSERT_TRUE (tcpSocket.opened ()); ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_TRUE (tcpSocket.opened ()); @@ -540,7 +543,7 @@ TEST_F (TcpSocket, connected) Tcp::Socket tcpSocket (Tcp::Socket::Blocking); ASSERT_FALSE (tcpSocket.connected ()); - ASSERT_EQ (tcpSocket.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tcpSocket.open (Tcp{IpAddress (_hostv4).family ()}), 0) << join::lastError.message (); ASSERT_FALSE (tcpSocket.connected ()); ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_TRUE (tcpSocket.connected ()); @@ -550,24 +553,6 @@ TEST_F (TcpSocket, connected) ASSERT_FALSE (tcpSocket.connected ()); } -/** - * @brief Test encrypted method. - */ -TEST_F (TcpSocket, encrypted) -{ - Tcp::Socket tcpSocket (Tcp::Socket::Blocking); - - ASSERT_FALSE (tcpSocket.encrypted ()); - ASSERT_EQ (tcpSocket.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - ASSERT_FALSE (tcpSocket.encrypted ()); - ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); - ASSERT_FALSE (tcpSocket.encrypted ()); - ASSERT_EQ (tcpSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (tcpSocket.encrypted ()); - tcpSocket.close (); - ASSERT_FALSE (tcpSocket.encrypted ()); -} - /** * @brief Test family method. */ @@ -614,7 +599,7 @@ TEST_F (TcpSocket, handle) Tcp::Socket tcpSocket (Tcp::Socket::Blocking); ASSERT_EQ (tcpSocket.handle (), -1); - ASSERT_EQ (tcpSocket.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tcpSocket.open (Tcp{IpAddress (_hostv4).family ()}), 0) << join::lastError.message (); ASSERT_GT (tcpSocket.handle (), -1); ASSERT_EQ (tcpSocket.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_GT (tcpSocket.handle (), -1); @@ -665,8 +650,8 @@ TEST_F (TcpSocket, lower) { Tcp::Socket tcpSocket1, tcpSocket2; - ASSERT_EQ (tcpSocket1.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - ASSERT_EQ (tcpSocket2.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tcpSocket1.open (Tcp{IpAddress (_hostv4).family ()}), 0) << join::lastError.message (); + ASSERT_EQ (tcpSocket2.open (Tcp{IpAddress (_hostv4).family ()}), 0) << join::lastError.message (); if (tcpSocket1.handle () < tcpSocket2.handle ()) { ASSERT_TRUE (tcpSocket1 < tcpSocket2); diff --git a/core/tests/threadpool_test.cpp b/core/tests/thread_pool_test.cpp similarity index 98% rename from core/tests/threadpool_test.cpp rename to core/tests/thread_pool_test.cpp index b554e3c8..4e8f5256 100644 --- a/core/tests/threadpool_test.cpp +++ b/core/tests/thread_pool_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include #include diff --git a/core/tests/udpsocket_test.cpp b/core/tests/udp_socket_test.cpp similarity index 94% rename from core/tests/udpsocket_test.cpp rename to core/tests/udp_socket_test.cpp index bbf46f5f..efeafd3e 100644 --- a/core/tests/udpsocket_test.cpp +++ b/core/tests/udp_socket_test.cpp @@ -23,8 +23,8 @@ */ // libjoin. +#include #include -#include // Libraries. #include @@ -39,7 +39,7 @@ using join::Udp; /** * @brief Class used to test the UDP socket API. */ -class UdpSocket : public Udp::Socket, public EventHandler, public ::testing::Test +class UdpSocket : public EventHandler, public ::testing::Test { protected: /** @@ -47,8 +47,8 @@ class UdpSocket : public Udp::Socket, public EventHandler, public ::testing::Tes */ void SetUp () override { - ASSERT_EQ (this->bind ({IpAddress::ipv6Wildcard, _port}), 0) << join::lastError.message (); - ASSERT_EQ (ReactorThread::reactor ().addHandler (handle (), this), 0) << join::lastError.message (); + ASSERT_EQ (_server.bind ({IpAddress::ipv6Wildcard, _port}), 0) << join::lastError.message (); + ASSERT_EQ (ReactorThread::reactor ().addHandler (_server.handle (), this), 0) << join::lastError.message (); } /** @@ -56,8 +56,8 @@ class UdpSocket : public Udp::Socket, public EventHandler, public ::testing::Tes */ void TearDown () override { - ASSERT_EQ (ReactorThread::reactor ().delHandler (handle ()), 0) << join::lastError.message (); - this->close (); + ASSERT_EQ (ReactorThread::reactor ().delHandler (_server.handle ()), 0) << join::lastError.message (); + _server.close (); } /** @@ -66,18 +66,21 @@ class UdpSocket : public Udp::Socket, public EventHandler, public ::testing::Tes */ virtual void onReadable ([[maybe_unused]] int fd) override { - auto buffer = std::make_unique (this->canRead ()); + auto buffer = std::make_unique (_server.canRead ()); if (buffer) { Udp::Endpoint from; - int nread = this->readFrom (buffer.get (), this->canRead (), &from); + int nread = _server.readFrom (buffer.get (), _server.canRead (), &from); if (nread > 0) { - this->writeTo (buffer.get (), nread, from); + _server.writeTo (buffer.get (), nread, from); } } } + /// server. + Udp::Socket _server; + /// host. static const std::string _host; @@ -459,22 +462,6 @@ TEST_F (UdpSocket, connected) ASSERT_FALSE (udpSocket.connected ()); } -/** - * @brief Test encrypted method. - */ -TEST_F (UdpSocket, encrypted) -{ - Udp::Socket udpSocket (Udp::Socket::Blocking); - - ASSERT_FALSE (udpSocket.opened ()); - ASSERT_EQ (udpSocket.open (Udp::v6 ()), 0) << join::lastError.message (); - ASSERT_FALSE (udpSocket.encrypted ()); - ASSERT_EQ (udpSocket.connect ({_host, _port}), 0) << join::lastError.message (); - ASSERT_FALSE (udpSocket.encrypted ()); - udpSocket.close (); - ASSERT_FALSE (udpSocket.encrypted ()); -} - /** * @brief Test family method. */ diff --git a/core/tests/unixdgramsocket_test.cpp b/core/tests/unix_datagram_socket_test.cpp similarity index 93% rename from core/tests/unixdgramsocket_test.cpp rename to core/tests/unix_datagram_socket_test.cpp index b9e1944b..900d346a 100644 --- a/core/tests/unixdgramsocket_test.cpp +++ b/core/tests/unix_datagram_socket_test.cpp @@ -23,8 +23,8 @@ */ // libjoin. +#include #include -#include // Libraries. #include @@ -37,7 +37,7 @@ using join::UnixDgram; /** * @brief Class used to test the unix datagram socket API. */ -class UnixDgramSocket : public UnixDgram::Socket, public EventHandler, public ::testing::Test +class UnixDgramSocket : public EventHandler, public ::testing::Test { protected: /** @@ -45,8 +45,8 @@ class UnixDgramSocket : public UnixDgram::Socket, public EventHandler, public :: */ void SetUp () override { - ASSERT_EQ (this->bind (_serverpath), 0) << join::lastError.message (); - ASSERT_EQ (ReactorThread::reactor ().addHandler (handle (), this), 0) << join::lastError.message (); + ASSERT_EQ (_server.bind (_serverpath), 0) << join::lastError.message (); + ASSERT_EQ (ReactorThread::reactor ().addHandler (_server.handle (), this), 0) << join::lastError.message (); } /** @@ -54,8 +54,8 @@ class UnixDgramSocket : public UnixDgram::Socket, public EventHandler, public :: */ void TearDown () override { - ASSERT_EQ (ReactorThread::reactor ().delHandler (handle ()), 0) << join::lastError.message (); - this->close (); + ASSERT_EQ (ReactorThread::reactor ().delHandler (_server.handle ()), 0) << join::lastError.message (); + _server.close (); } /** @@ -64,18 +64,21 @@ class UnixDgramSocket : public UnixDgram::Socket, public EventHandler, public :: */ virtual void onReadable ([[maybe_unused]] int fd) override { - auto buffer = std::make_unique (this->canRead ()); + auto buffer = std::make_unique (_server.canRead ()); if (buffer) { UnixDgram::Endpoint from; - int nread = this->readFrom (buffer.get (), this->canRead (), &from); + int nread = _server.readFrom (buffer.get (), _server.canRead (), &from); if (nread > 0) { - this->writeTo (buffer.get (), nread, from); + _server.writeTo (buffer.get (), nread, from); } } } + /// server. + UnixDgram::Socket _server; + /// path. static const std::string _serverpath; static const std::string _clientpath; @@ -424,22 +427,6 @@ TEST_F (UnixDgramSocket, connected) ASSERT_FALSE (unixSocket.connected ()); } -/** - * @brief Test encrypted method. - */ -TEST_F (UnixDgramSocket, encrypted) -{ - UnixDgram::Socket unixSocket (UnixDgram::Socket::Blocking); - - ASSERT_FALSE (unixSocket.opened ()); - ASSERT_EQ (unixSocket.open (), 0) << join::lastError.message (); - ASSERT_FALSE (unixSocket.encrypted ()); - ASSERT_EQ (unixSocket.connect (_serverpath), 0) << join::lastError.message (); - ASSERT_FALSE (unixSocket.encrypted ()); - unixSocket.close (); - ASSERT_FALSE (unixSocket.encrypted ()); -} - /** * @brief Test family method. */ diff --git a/core/tests/unixstreamacceptor_test.cpp b/core/tests/unix_stream_acceptor_test.cpp similarity index 100% rename from core/tests/unixstreamacceptor_test.cpp rename to core/tests/unix_stream_acceptor_test.cpp diff --git a/core/tests/unixstreamsocket_test.cpp b/core/tests/unix_stream_socket_test.cpp similarity index 95% rename from core/tests/unixstreamsocket_test.cpp rename to core/tests/unix_stream_socket_test.cpp index f5fc668f..b44741ce 100644 --- a/core/tests/unixstreamsocket_test.cpp +++ b/core/tests/unix_stream_socket_test.cpp @@ -37,7 +37,7 @@ using join::UnixStream; /** * @brief Class used to test the unix stream socket API. */ -class UnixStreamSocket : public UnixStream::Acceptor, public EventHandler, public ::testing::Test +class UnixStreamSocket : public EventHandler, public ::testing::Test { protected: /** @@ -45,8 +45,8 @@ class UnixStreamSocket : public UnixStream::Acceptor, public EventHandler, publi */ void SetUp () override { - ASSERT_EQ (this->create (_serverpath), 0) << join::lastError.message (); - ASSERT_EQ (ReactorThread::reactor ().addHandler (handle (), this), 0) << join::lastError.message (); + ASSERT_EQ (_server.create (_serverpath), 0) << join::lastError.message (); + ASSERT_EQ (ReactorThread::reactor ().addHandler (_server.handle (), this), 0) << join::lastError.message (); } /** @@ -54,8 +54,8 @@ class UnixStreamSocket : public UnixStream::Acceptor, public EventHandler, publi */ void TearDown () override { - ASSERT_EQ (ReactorThread::reactor ().delHandler (handle ()), 0) << join::lastError.message (); - this->close (); + ASSERT_EQ (ReactorThread::reactor ().delHandler (_server.handle ()), 0) << join::lastError.message (); + _server.close (); } /** @@ -64,7 +64,7 @@ class UnixStreamSocket : public UnixStream::Acceptor, public EventHandler, publi */ virtual void onReadable ([[maybe_unused]] int fd) override { - UnixStream::Socket sock = this->accept (); + UnixStream::Socket sock = _server.accept (); if (sock.connected ()) { char buf[1024]; @@ -87,6 +87,9 @@ class UnixStreamSocket : public UnixStream::Acceptor, public EventHandler, publi } } + /// server. + UnixStream::Acceptor _server; + /// path. static const std::string _serverpath; static const std::string _clientpath; @@ -502,24 +505,6 @@ TEST_F (UnixStreamSocket, connected) ASSERT_FALSE (unixSocket.connected ()); } -/** - * @brief Test encrypted method. - */ -TEST_F (UnixStreamSocket, encrypted) -{ - UnixStream::Socket unixSocket (UnixStream::Socket::Blocking); - - ASSERT_FALSE (unixSocket.encrypted ()); - ASSERT_EQ (unixSocket.open (), 0) << join::lastError.message (); - ASSERT_FALSE (unixSocket.encrypted ()); - ASSERT_EQ (unixSocket.connect (_serverpath), 0) << join::lastError.message (); - ASSERT_FALSE (unixSocket.encrypted ()); - ASSERT_EQ (unixSocket.disconnect (), 0) << join::lastError.message (); - ASSERT_FALSE (unixSocket.encrypted ()); - unixSocket.close (); - ASSERT_FALSE (unixSocket.encrypted ()); -} - /** * @brief Test family method. */ diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index feb1afb8..c4a6fcbd 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -7,11 +7,15 @@ set(PUBLIC_HEADERS include/join/openssl.hpp include/join/openssl.hpp include/join/hmac.hpp - include/join/tlskey.hpp + include/join/tls_key.hpp include/join/signature.hpp - include/join/tlserror.hpp - include/join/tlscontext.hpp - include/join/tlswrapper.hpp + include/join/tl_serror.hpp + include/join/tls_context.hpp + include/join/tls.hpp + include/join/tls_wrapper.hpp + include/join/tls_stream.hpp + include/join/dtls_wrapper.hpp + include/join/tls_protocol.hpp ) set(SOURCES @@ -19,10 +23,10 @@ set(SOURCES src/openssl.cpp src/digest.cpp src/hmac.cpp - src/tlskey.cpp + src/tls_key.cpp src/signature.cpp - src/tlserror.cpp - src/tlscontext.cpp + src/tls_error.cpp + src/tls_context.cpp ) add_library(${JOIN_CRYPTO} ${SOURCES}) diff --git a/crypto/include/join/dtls_wrapper.hpp b/crypto/include/join/dtls_wrapper.hpp new file mode 100644 index 00000000..003ebf84 --- /dev/null +++ b/crypto/include/join/dtls_wrapper.hpp @@ -0,0 +1,156 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_CRYPTO_DTLS_WRAPPER_HPP +#define JOIN_CRYPTO_DTLS_WRAPPER_HPP + +// libjoin. +#include + +namespace join +{ + /** + * @brief DTLS decorator for datagram sockets. + */ + template + class BasicDtlsWrapper : public BasicTls + { + public: + using Endpoint = typename BasicTls::Endpoint; + + /// inherit the base constructors. + using BasicTls::BasicTls; + + /** + * @brief read data on the socket. + * @param buf buffer used to store the data received. + * @param len maximum number of bytes to read. + * @param endpoint endpoint from where data are coming (optional). + * @return The number of bytes received, -1 on failure. + */ + int readFrom (char* buf, unsigned long len, Endpoint* endpoint = nullptr) noexcept + { + if (this->encrypted ()) + { + int nread = SSL_read (this->_ssl.get (), buf, static_cast (len)); + if (nread < 1) + { + return this->handleTlsError (nread); + } + + if (endpoint != nullptr) + { + BIO* rbio = SSL_get_rbio (this->_ssl.get ()); + if (!rbio) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + struct sockaddr_storage sa; + socklen_t sa_len = sizeof (struct sockaddr_storage); + if (BIO_dgram_get_peer (rbio, &sa) <= 0) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + *endpoint = Endpoint (reinterpret_cast (&sa), sa_len); + } + + return nread; + } + + return this->_socket.readFrom (buf, len, endpoint); + } + + /** + * @brief write data on the socket. + * @param buf buffer to write from. + * @param len number of bytes to write. + * @param endpoint endpoint where to write the data. + * @return the number of bytes written, -1 on failure. + */ + int writeTo (const char* buf, unsigned long len, const Endpoint& endpoint) noexcept + { + if (this->encrypted ()) + { + BIO* wbio = SSL_get_wbio (this->_ssl.get ()); + if (!wbio) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + struct sockaddr_storage sa; + socklen_t sa_len = sizeof (struct sockaddr_storage); + if (BIO_dgram_get_peer (wbio, &sa) <= 0) + { + lastError = make_error_code (Errc::OperationFailed); + return -1; + } + + Endpoint remote (reinterpret_cast (&sa), sa_len); + if (remote != endpoint) + { + lastError = make_error_code (Errc::InvalidParam); + return -1; + } + + int nwritten = SSL_write (this->_ssl.get (), buf, static_cast (len)); + if (nwritten < 1) + { + return this->handleTlsError (nwritten); + } + + return nwritten; + } + + return this->_socket.writeTo (buf, len, endpoint); + } + + /** + * @brief returns the Time-To-Live value. + * @return The Time-To-Live value. + */ + int ttl () const noexcept + { + return this->_socket.ttl (); + } + }; + + /** + * @brief compare two DTLS decorators based on their underlying socket handle. + * @param a first DTLS decorator. + * @param b second DTLS decorator. + * @return true if the handle of a is less than the handle of b, false otherwise. + */ + template + inline bool operator< (const BasicDtlsWrapper& a, const BasicDtlsWrapper& b) noexcept + { + return a.handle () < b.handle (); + } +} + +#endif diff --git a/crypto/include/join/openssl.hpp b/crypto/include/join/openssl.hpp index d3f9d3ff..224fdcc6 100644 --- a/crypto/include/join/openssl.hpp +++ b/crypto/include/join/openssl.hpp @@ -34,6 +34,7 @@ #include #include #include +#include // C++. #include diff --git a/crypto/include/join/tlswrapper.hpp b/crypto/include/join/tls.hpp similarity index 79% rename from crypto/include/join/tlswrapper.hpp rename to crypto/include/join/tls.hpp index 85a66b2d..7efd033b 100644 --- a/crypto/include/join/tlswrapper.hpp +++ b/crypto/include/join/tls.hpp @@ -22,49 +22,57 @@ * SOFTWARE. */ -#ifndef JOIN_CORE_TLS_WRAPPER_HPP -#define JOIN_CORE_TLS_WRAPPER_HPP +#ifndef JOIN_CRYPTO_TLS_HPP +#define JOIN_CRYPTO_TLS_HPP // libjoin. -#include -#include -#include +#include +#include +#include +#include +#include #include +// C++. +#include +#include +#include + // C. #include +#include namespace join { /** - * @brief TLS/DTLS decorator. + * @brief basic TLS/DTLS decorator. */ - template - class TlsWrapper + template + class BasicTls { public: - using UnderlyingSocket = Socket; - using Protocol = typename Socket::Proto; - using Mode = typename Socket::Mode; - using Option = typename Socket::Option; - using State = typename Socket::State; - using Endpoint = typename Socket::Endpoint; + using UnderlyingSocket = typename Protocol::Transport::Socket; + using Mode = typename UnderlyingSocket::Mode; + using Option = typename UnderlyingSocket::Option; + using State = typename UnderlyingSocket::State; + using Endpoint = typename UnderlyingSocket::Endpoint; /** - * @brief create a TLS wrapper with an internally created socket. + * @brief create a TLS decorator with an internally created socket. * @param ctx TLS context. + * @param mode blocking mode. */ - explicit TlsWrapper (TlsContext ctx, Mode mode = Mode::NonBlocking) noexcept - : TlsWrapper (Socket{mode}, ctx) + explicit BasicTls (TlsContext ctx, Mode mode = Mode::NonBlocking) noexcept + : BasicTls (UnderlyingSocket{mode}, ctx) { } /** - * @brief create a TLS wrapper taking ownership of the given socket. + * @brief create a TLS decorator taking ownership of the given socket. * @param socket underlying socket. * @param ctx TLS context. */ - TlsWrapper (Socket&& socket, TlsContext ctx) noexcept + BasicTls (UnderlyingSocket&& socket, TlsContext ctx) noexcept : _socket (std::move (socket)) , _ctx (ctx) { @@ -74,20 +82,20 @@ namespace join * @brief copy constructor. * @param other other object to copy. */ - TlsWrapper (const TlsWrapper&) = delete; + BasicTls (const BasicTls& other) = delete; /** * @brief copy assignment operator. * @param other other object to assign. * @return assigned object. */ - TlsWrapper& operator= (const TlsWrapper&) = delete; + BasicTls& operator= (const BasicTls& other) = delete; /** * @brief move constructor. * @param other other object to move. */ - TlsWrapper (TlsWrapper&& other) noexcept + BasicTls (BasicTls&& other) noexcept : _socket (std::move (other._socket)) , _ctx (std::move (other._ctx)) , _ssl (std::move (other._ssl)) @@ -103,7 +111,7 @@ namespace join * @param other other object to assign. * @return assigned object. */ - TlsWrapper& operator= (TlsWrapper&& other) noexcept + BasicTls& operator= (BasicTls&& other) noexcept { _socket = std::move (other._socket); _ctx = std::move (other._ctx); @@ -118,9 +126,9 @@ namespace join } /** - * @brief destroy TLS stream. + * @brief destroy the instance. */ - ~TlsWrapper () = default; + virtual ~BasicTls () = default; /** * @brief open the underlying socket using the given protocol. @@ -129,7 +137,7 @@ namespace join */ int open (const Protocol& protocol = Protocol ()) noexcept { - return _socket.open (protocol); + return _socket.open (typename Protocol::Transport (protocol.family ())); } /** @@ -143,7 +151,7 @@ namespace join /** * @brief assigns the specified endpoint to the underlying socket. - * @param endpoint endpoint to assign to the underlying socket. + * @param ep endpoint to assign to the underlying socket. * @return 0 on success, -1 on failure. */ int bind (const Endpoint& ep) noexcept @@ -153,7 +161,7 @@ namespace join /** * @brief assigns the specified device to the underlying socket. - * @param device device name. + * @param dev device name. * @return 0 on success, -1 on failure. */ int bindToDevice (const std::string& dev) noexcept @@ -162,51 +170,16 @@ namespace join } /** - * @brief connect the the underlying socket to the remote endpoint. - * @param endpoint endpoint to connect to. + * @brief connect the underlying socket to the remote endpoint. + * @param ep endpoint to connect to. * @return 0 on success, -1 on failure. + * @note transport connection only, the TLS handshake is performed separately. */ - int connect (const Endpoint& ep) + int connect (const Endpoint& ep) noexcept { return _socket.connect (ep); } - /** - * @brief check if the underlying socket is connecting. - * @return true if connecting. - */ - bool connecting () const noexcept - { - return _socket.connecting (); - } - - /** - * @brief block until the underlying socket is connected. - * @param timeout timeout in milliseconds. - * @return true if connected, false otherwise. - */ - template ::type = 0> - bool waitConnected (int timeout = 0) - { - return _socket.waitConnected (timeout); - } - - /** - * @brief block until the underlying socket is connected. - * @param timeout timeout in milliseconds. - * @return true if connected, false otherwise. - */ - template ::type = 0> - bool waitConnected ([[maybe_unused]] int timeout = 0) - { - if (_ctx.isServer ()) - { - return true; - } - - return _socket.connected (); - } - /** * @brief check if the underlying socket is connected. * @return true if connected. @@ -220,7 +193,7 @@ namespace join * @brief Perform the TLS handshake. * @return 0 on success, -1 on failure. */ - int handshake () noexcept + int handshake () { if (encrypted ()) { @@ -327,7 +300,6 @@ namespace join int result = SSL_do_handshake (_ssl.get ()); if (result < 1) { - // return handleTlsError (result); int ret = handleTlsError (result); if (lastError != make_error_code (Errc::TemporaryError)) { @@ -340,17 +312,12 @@ namespace join } /** - * @brief block until TLS handshake is finnished. + * @brief block until TLS handshake is finished. * @param timeout timeout in milliseconds (0: infinite). - * @return true if TLS handshake is finnished. + * @return true if TLS handshake is finished. */ - bool waitHandshake (int timeout) noexcept + virtual bool waitHandshake (int timeout) { - if (!waitConnected (timeout)) - { - return false; - } - if (handshake () == 0) { return true; @@ -470,9 +437,9 @@ namespace join } /** - * @brief block until TLS shutdown is finnished. + * @brief block until TLS shutdown is finished. * @param timeout timeout in milliseconds (0: infinite). - * @return true if TLS shutdown is finnished. + * @return true if TLS shutdown is finished. */ bool waitShutdown (int timeout) noexcept { @@ -514,38 +481,11 @@ namespace join * @brief disconnect the underlying socket from the remote endpoint. * @return 0 on success, -1 on failure. */ - int disconnect () + int disconnect () noexcept { return _socket.disconnect (); } - /** - * @brief block until the underlying socket is disconnected. - * @param timeout timeout in milliseconds. - * @return true if disconnected, false otherwise. - */ - template ::type = 0> - bool waitDisconnected (int timeout = 0) - { - return _socket.waitDisconnected (timeout); - } - - /** - * @brief block until the underlying socket is disconnected. - * @param timeout timeout in milliseconds. - * @return true if disconnected, false otherwise. - */ - template ::type = 0> - bool waitDisconnected ([[maybe_unused]] int timeout) - { - if (_ctx.isServer ()) - { - return true; - } - - return !_socket.connected (); - } - /** * @brief close the socket handle. */ @@ -576,49 +516,6 @@ namespace join return _socket.waitReadyRead (timeout); } - /** - * @brief read data on the socket. - * @param buf buffer used to store the data received. - * @param len maximum number of bytes to read. - * @param endpoint endpoint from where data are coming (optional). - * @return The number of bytes received, -1 on failure. - */ - int readFrom (char* buf, unsigned long len, Endpoint* endpoint = nullptr) noexcept - { - if (encrypted ()) - { - int nread = SSL_read (_ssl.get (), buf, static_cast (len)); - if (nread < 1) - { - return handleTlsError (nread); - } - - if (endpoint != nullptr) - { - BIO* rbio = SSL_get_rbio (_ssl.get ()); - if (!rbio) - { - lastError = make_error_code (Errc::OperationFailed); - return -1; - } - - struct sockaddr_storage sa; - socklen_t sa_len = sizeof (struct sockaddr_storage); - if (BIO_dgram_get_peer (rbio, &sa) <= 0) - { - lastError = make_error_code (Errc::OperationFailed); - return -1; - } - - *endpoint = Endpoint (reinterpret_cast (&sa), sa_len); - } - - return nread; - } - - return _socket.readFrom (buf, len, endpoint); - } - /** * @brief read data from the TLS stream. * @param buf buffer to read into. @@ -660,8 +557,11 @@ namespace join if (lastError == Errc::TemporaryError) { if (waitReadyRead (timeout)) + { continue; + } } + return -1; } @@ -672,7 +572,7 @@ namespace join } /** - * @brief block until until at least one byte can be written on the socket. + * @brief block until at least one byte can be written on the socket. * @param timeout timeout in milliseconds (0: infinite). * @return true if data can be written on the socket, false otherwise. */ @@ -692,51 +592,6 @@ namespace join return _socket.waitReadyWrite (timeout); } - /** - * @brief write data on the socket. - * @param buf buffer to write from. - * @param len number of bytes to write. - * @param endpoint endpoint where to write the data. - * @return the number of bytes written, -1 on failure. - */ - int writeTo (const char* buf, unsigned long len, const Endpoint& endpoint) noexcept - { - if (encrypted ()) - { - BIO* wbio = SSL_get_wbio (_ssl.get ()); - if (!wbio) - { - lastError = make_error_code (Errc::OperationFailed); - return -1; - } - - struct sockaddr_storage sa; - socklen_t sa_len = sizeof (struct sockaddr_storage); - if (BIO_dgram_get_peer (wbio, &sa) <= 0) - { - lastError = make_error_code (Errc::OperationFailed); - return -1; - } - - Endpoint remote (reinterpret_cast (&sa), sa_len); - if (remote != endpoint) - { - lastError = make_error_code (Errc::InvalidParam); - return -1; - } - - int nwritten = SSL_write (_ssl.get (), buf, static_cast (len)); - if (nwritten < 1) - { - return handleTlsError (nwritten); - } - - return nwritten; - } - - return _socket.writeTo (buf, len, endpoint); - } - /** * @brief write data to the TLS stream. * @param buf buffer to write from. @@ -778,8 +633,11 @@ namespace join if (lastError == Errc::TemporaryError) { if (waitReadyWrite (timeout)) + { continue; + } } + return -1; } @@ -849,20 +707,11 @@ namespace join * @brief get the maximum transmission unit. * @return MTU. */ - int mtu () const + int mtu () const noexcept { return _socket.mtu (); } - /** - * @brief returns the Time-To-Live value. - * @return The Time-To-Live value. - */ - int ttl () const noexcept - { - return _socket.ttl (); - } - /** * @brief get the local endpoint. * @return local endpoint. @@ -875,16 +724,17 @@ namespace join /** * @brief get the remote endpoint. * @return remote endpoint. + * @note returns by value, copying the endpoint hostname may allocate. */ - Endpoint remoteEndpoint () const noexcept + Endpoint remoteEndpoint () const { return _socket.remoteEndpoint (); } - private: + protected: /** - * @brief handle TLS error - * @param error result returned by a previous call of the openssl API. + * @brief handle TLS error. + * @param result result returned by a previous call of the openssl API. * @return -1 is always returned. */ int handleTlsError (int result) noexcept @@ -937,7 +787,7 @@ namespace join static void infoWrapper (const SSL* ssl, int where, int ret) noexcept { assert (ssl); - static_cast*> (SSL_get_app_data (ssl))->infoCallback (where, ret); + static_cast*> (SSL_get_app_data (ssl))->infoCallback (where, ret); } /** @@ -999,13 +849,13 @@ namespace join { SSL* ssl = static_cast (X509_STORE_CTX_get_ex_data (x509Ctx, SSL_get_ex_data_X509_STORE_CTX_idx ())); assert (ssl); - return static_cast*> (SSL_get_app_data (ssl))->verifyCallback (preverified, x509Ctx); + return static_cast*> (SSL_get_app_data (ssl))->verifyCallback (preverified, x509Ctx); } /** * @brief verify peer certificate. * @param preverified pre-verification status. - * @param x509Ctx X509 store context. + * @param context X509 store context. * @return 1 if verified, 0 otherwise. */ int verifyCallback (int preverified, X509_STORE_CTX* context) noexcept @@ -1174,7 +1024,7 @@ namespace join }*/ /// underlying socket. - Socket _socket; + UnderlyingSocket _socket; /// TLS context. TlsContext _ctx; @@ -1184,13 +1034,13 @@ namespace join }; /** - * @brief compare two TLS streams based on their underlying socket handle. - * @param a first TLS stream. - * @param b second TLS stream. + * @brief compare two TLS decorators based on their underlying socket handle. + * @param a first TLS decorator. + * @param b second TLS decorator. * @return true if the handle of a is less than the handle of b, false otherwise. */ - template - constexpr bool operator< (const TlsWrapper& a, const TlsWrapper& b) noexcept + template + inline bool operator< (const BasicTls& a, const BasicTls& b) noexcept { return a.handle () < b.handle (); } diff --git a/crypto/include/join/tlscontext.hpp b/crypto/include/join/tls_context.hpp similarity index 98% rename from crypto/include/join/tlscontext.hpp rename to crypto/include/join/tls_context.hpp index 69a7f150..811cc522 100644 --- a/crypto/include/join/tlscontext.hpp +++ b/crypto/include/join/tls_context.hpp @@ -22,11 +22,11 @@ * SOFTWARE. */ -#ifndef JOIN_CORE_TLS_CONTEXT_HPP -#define JOIN_CORE_TLS_CONTEXT_HPP +#ifndef JOIN_CRYPTO_TLS_CONTEXT_HPP +#define JOIN_CRYPTO_TLS_CONTEXT_HPP // libjoin. -// #include +#include #include namespace join diff --git a/crypto/include/join/tlserror.hpp b/crypto/include/join/tls_error.hpp similarity index 97% rename from crypto/include/join/tlserror.hpp rename to crypto/include/join/tls_error.hpp index 32b08990..14fc0874 100644 --- a/crypto/include/join/tlserror.hpp +++ b/crypto/include/join/tls_error.hpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#ifndef JOIN_CORE_TLS_ERROR_HPP -#define JOIN_CORE_TLS_ERROR_HPP +#ifndef JOIN_CRYPTO_TLS_ERROR_HPP +#define JOIN_CRYPTO_TLS_ERROR_HPP // C++. #include diff --git a/crypto/include/join/tlskey.hpp b/crypto/include/join/tls_key.hpp similarity index 98% rename from crypto/include/join/tlskey.hpp rename to crypto/include/join/tls_key.hpp index 37b8c789..9ac01912 100644 --- a/crypto/include/join/tlskey.hpp +++ b/crypto/include/join/tls_key.hpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#ifndef TLS_CRYPTO_KEY_HPP -#define TLS_CRYPTO_KEY_HPP +#ifndef JOIN_CRYPTO_TLS_KEY_HPP +#define JOIN_CRYPTO_TLS_KEY_HPP // libjoin. #include diff --git a/crypto/include/join/tls_protocol.hpp b/crypto/include/join/tls_protocol.hpp new file mode 100644 index 00000000..6907bc3e --- /dev/null +++ b/crypto/include/join/tls_protocol.hpp @@ -0,0 +1,230 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_CRYPTO_TLS_PROTOCOL_HPP +#define JOIN_CRYPTO_TLS_PROTOCOL_HPP + +// libjoin. +#include + +namespace join +{ + template + class BasicTlsWrapper; + + template + class BasicDtlsWrapper; + + template + class BasicTlsStream; + + /** + * @brief TLS protocol class (TLS over TCP). + */ + class Tls + { + public: + using Transport = Tcp; + using Endpoint = BasicInternetEndpoint; + using Socket = BasicTlsWrapper; + using Stream = BasicTlsStream; + + /** + * @brief create the tls protocol instance. + * @param family IP address family. + */ + constexpr explicit Tls (int family = AF_INET) noexcept + : _family (family) + { + } + + /** + * @brief get protocol suitable for IPv4 address family. + * @return an IPv4 address family suitable protocol. + */ + static inline Tls& v4 () noexcept + { + static Tls tlsv4 (AF_INET); + return tlsv4; + } + + /** + * @brief get protocol suitable for IPv6 address family. + * @return an IPv6 address family suitable protocol. + */ + static inline Tls& v6 () noexcept + { + static Tls tlsv6 (AF_INET6); + return tlsv6; + } + + /** + * @brief get the protocol IP address family. + * @return the protocol IP address family. + */ + constexpr int family () const noexcept + { + return _family; + } + + /** + * @brief get the protocol communication semantic. + * @return the protocol communication semantic. + */ + constexpr int type () const noexcept + { + return SOCK_STREAM; + } + + /** + * @brief get the protocol type. + * @return the protocol type. + */ + constexpr int protocol () const noexcept + { + return IPPROTO_TCP; + } + + private: + /// IP address family. + int _family; + }; + + /** + * @brief check if equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if equals. + */ + constexpr bool operator== (const Tls& a, const Tls& b) noexcept + { + return a.family () == b.family (); + } + + /** + * @brief check if not equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if not equals. + */ + constexpr bool operator!= (const Tls& a, const Tls& b) noexcept + { + return !(a == b); + } + + /** + * @brief DTLS protocol class (DTLS over UDP). + */ + class Dtls + { + public: + using Transport = Udp; + using Endpoint = BasicInternetEndpoint; + using Socket = BasicDtlsWrapper; + + /** + * @brief create the dtls protocol instance. + * @param family IP address family. + */ + constexpr explicit Dtls (int family = AF_INET) noexcept + : _family (family) + { + } + + /** + * @brief get protocol suitable for IPv4 address family. + * @return an IPv4 address family suitable protocol. + */ + static inline Dtls& v4 () noexcept + { + static Dtls dtlsv4 (AF_INET); + return dtlsv4; + } + + /** + * @brief get protocol suitable for IPv6 address family. + * @return an IPv6 address family suitable protocol. + */ + static inline Dtls& v6 () noexcept + { + static Dtls dtlsv6 (AF_INET6); + return dtlsv6; + } + + /** + * @brief get the protocol IP address family. + * @return the protocol IP address family. + */ + constexpr int family () const noexcept + { + return _family; + } + + /** + * @brief get the protocol communication semantic. + * @return the protocol communication semantic. + */ + constexpr int type () const noexcept + { + return SOCK_DGRAM; + } + + /** + * @brief get the protocol type. + * @return the protocol type. + */ + constexpr int protocol () const noexcept + { + return IPPROTO_UDP; + } + + private: + /// IP address family. + int _family; + }; + + /** + * @brief check if equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if equals. + */ + constexpr bool operator== (const Dtls& a, const Dtls& b) noexcept + { + return a.family () == b.family (); + } + + /** + * @brief check if not equals. + * @param a protocol to check. + * @param b protocol to check. + * @return true if not equals. + */ + constexpr bool operator!= (const Dtls& a, const Dtls& b) noexcept + { + return !(a == b); + } +} + +#endif diff --git a/crypto/include/join/tls_stream.hpp b/crypto/include/join/tls_stream.hpp new file mode 100644 index 00000000..4551ac41 --- /dev/null +++ b/crypto/include/join/tls_stream.hpp @@ -0,0 +1,175 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_CRYPTO_TLS_STREAM_HPP +#define JOIN_CRYPTO_TLS_STREAM_HPP + +// libjoin. +#include +#include + +// C++. +#include +#include + +namespace join +{ + /** + * @brief TLS stream class. + */ + template + class BasicTlsStream : public BasicSocketStream + { + public: + using Endpoint = typename Protocol::Endpoint; + using Socket = typename Protocol::Socket; + using Transport = typename Protocol::Transport::Socket; + using Mode = typename Socket::Mode; + + /** + * @brief default constructor. + */ + BasicTlsStream () + : BasicTlsStream (TlsContext{}) + { + } + + /** + * @brief construct the TLS stream instance using the given context. + * @param ctx TLS context to use. + * @param mode socket blocking mode. + */ + explicit BasicTlsStream (TlsContext ctx, Mode mode = Mode::NonBlocking) + : BasicSocketStream (Socket{std::move (ctx), mode}) + { + } + + /** + * @brief construct the TLS stream by moving an already wrapped TLS socket in. + * @param socket TLS socket to move in. + */ + explicit BasicTlsStream (Socket&& socket) + : BasicSocketStream (std::move (socket)) + { + } + + /** + * @brief construct the TLS stream by wrapping an already connected transport socket. + * @param socket connected transport socket to move in (e.g. as returned by accept ()). + * @param ctx TLS context to use for the encryption layer. + */ + explicit BasicTlsStream (Transport&& socket, TlsContext ctx = TlsContext{}) + : BasicSocketStream (Socket{std::move (socket), std::move (ctx)}) + { + } + + /** + * @brief copy constructor. + * @param other other object to copy. + */ + BasicTlsStream (const BasicTlsStream& other) = delete; + + /** + * @brief copy assignment operator. + * @param other other object to assign. + * @return current object. + */ + BasicTlsStream& operator= (const BasicTlsStream& other) = delete; + + /** + * @brief move constructor. + * @param other other object to move. + */ + BasicTlsStream (BasicTlsStream&& other) + : BasicSocketStream (std::move (other)) + { + } + + /** + * @brief move assignment operator. + * @param other other object to assign. + * @return current object. + */ + BasicTlsStream& operator= (BasicTlsStream&& other) + { + BasicSocketStream::operator= (std::move (other)); + + return *this; + } + + /** + * @brief destroy the TLS stream instance. + */ + virtual ~BasicTlsStream () = default; + + /** + * @brief perform the TLS handshake. + */ + void handshake () + { + if (this->_sockbuf.socket ().handshake () == -1) + { + if (lastError == Errc::TemporaryError) + { + if (this->_sockbuf.socket ().waitHandshake (this->timeout ())) + { + return; + } + } + + this->setstate (std::ios_base::failbit); + } + } + + /** + * @brief perform the TLS shutdown (send a close_notify alert). + */ + void shutdown () + { + if (this->_sockbuf.socket ().shutdown () == -1) + { + if (lastError == Errc::TemporaryError) + { + if (this->_sockbuf.socket ().waitShutdown (this->timeout ())) + { + return; + } + } + + this->setstate (std::ios_base::failbit); + } + } + + /** + * @brief check if the stream is encrypted. + * @return true if the stream is encrypted, false otherwise. + */ + bool encrypted () + { + return this->_sockbuf.socket ().encrypted (); + } + }; +} + +#endif diff --git a/crypto/include/join/tls_wrapper.hpp b/crypto/include/join/tls_wrapper.hpp new file mode 100644 index 00000000..66c1a97d --- /dev/null +++ b/crypto/include/join/tls_wrapper.hpp @@ -0,0 +1,104 @@ +/** + * MIT License + * + * Copyright (c) 2026 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef JOIN_CRYPTO_TLS_WRAPPER_HPP +#define JOIN_CRYPTO_TLS_WRAPPER_HPP + +// libjoin. +#include + +namespace join +{ + /** + * @brief TLS decorator for stream sockets. + */ + template + class BasicTlsWrapper : public BasicTls + { + public: + using Endpoint = typename BasicTls::Endpoint; + + /// inherit the base constructors. + using BasicTls::BasicTls; + + /** + * @brief check if the underlying socket is connecting. + * @return true if connecting. + */ + bool connecting () const noexcept + { + return this->_socket.connecting (); + } + + /** + * @brief block until the underlying socket is connected. + * @param timeout timeout in milliseconds. + * @return true if connected, false otherwise. + */ + bool waitConnected (int timeout = 0) + { + return this->_socket.waitConnected (timeout); + } + + /** + * @brief block until the underlying socket is disconnected. + * @param timeout timeout in milliseconds. + * @return true if disconnected, false otherwise. + */ + bool waitDisconnected (int timeout = 0) + { + return this->_socket.waitDisconnected (timeout); + } + + /** + * @brief block until TLS handshake is finished. + * @param timeout timeout in milliseconds (0: infinite). + * @return true if TLS handshake is finished. + * @note waits for the transport connection first, then runs the common handshake. + */ + bool waitHandshake (int timeout) override + { + if (!this->_socket.waitConnected (timeout)) + { + return false; + } + + return BasicTls::waitHandshake (timeout); + } + }; + + /** + * @brief compare two TLS decorators based on their underlying socket handle. + * @param a first TLS decorator. + * @param b second TLS decorator. + * @return true if the handle of a is less than the handle of b, false otherwise. + */ + template + inline bool operator< (const BasicTlsWrapper& a, const BasicTlsWrapper& b) noexcept + { + return a.handle () < b.handle (); + } +} + +#endif diff --git a/crypto/src/openssl.cpp b/crypto/src/openssl.cpp index 90dc58ee..bada9894 100644 --- a/crypto/src/openssl.cpp +++ b/crypto/src/openssl.cpp @@ -28,7 +28,6 @@ // Libraries. #include #include -#include // C++. #include diff --git a/crypto/src/signature.cpp b/crypto/src/signature.cpp index f4f32727..053936d3 100644 --- a/crypto/src/signature.cpp +++ b/crypto/src/signature.cpp @@ -24,7 +24,7 @@ // libjoin. #include -#include +#include #include #include diff --git a/crypto/src/tlscontext.cpp b/crypto/src/tls_context.cpp similarity index 99% rename from crypto/src/tlscontext.cpp rename to crypto/src/tls_context.cpp index 88b86228..2bac631b 100644 --- a/crypto/src/tlscontext.cpp +++ b/crypto/src/tls_context.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include // C. diff --git a/crypto/src/tlserror.cpp b/crypto/src/tls_error.cpp similarity index 99% rename from crypto/src/tlserror.cpp rename to crypto/src/tls_error.cpp index 13f4edad..9cf78628 100644 --- a/crypto/src/tlserror.cpp +++ b/crypto/src/tls_error.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include using join::TlsCategory; using join::TlsErrc; diff --git a/crypto/src/tlskey.cpp b/crypto/src/tls_key.cpp similarity index 99% rename from crypto/src/tlskey.cpp rename to crypto/src/tls_key.cpp index 296af590..73cbce49 100644 --- a/crypto/src/tlskey.cpp +++ b/crypto/src/tls_key.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include #include #include diff --git a/crypto/tests/CMakeLists.txt b/crypto/tests/CMakeLists.txt index 61feb6b2..0f86aef2 100644 --- a/crypto/tests/CMakeLists.txt +++ b/crypto/tests/CMakeLists.txt @@ -12,10 +12,10 @@ target_link_libraries(openssl.gtest ${JOIN_CRYPTO} GTest::gtest_main) add_test(NAME openssl.gtest COMMAND openssl.gtest) install(TARGETS openssl.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(digesterror.gtest digesterror_test.cpp) -target_link_libraries(digesterror.gtest ${JOIN_CRYPTO} GTest::gtest_main) -add_test(NAME digesterror.gtest COMMAND digesterror.gtest) -install(TARGETS digesterror.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(digest_error.gtest digest_error_test.cpp) +target_link_libraries(digest_error.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME digest_error.gtest COMMAND digest_error.gtest) +install(TARGETS digest_error.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(digest.gtest digest_test.cpp) target_link_libraries(digest.gtest ${JOIN_CRYPTO} GTest::gtest_main) @@ -27,32 +27,42 @@ target_link_libraries(hmac.gtest ${JOIN_CRYPTO} GTest::gtest_main) add_test(NAME hmac.gtest COMMAND hmac.gtest) install(TARGETS hmac.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(tlskey.gtest tlskey_test.cpp) -target_link_libraries(tlskey.gtest ${JOIN_CRYPTO} GTest::gtest_main) -add_test(NAME tlskey.gtest COMMAND tlskey.gtest) -install(TARGETS tlskey.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(tls_key.gtest tls_key_test.cpp) +target_link_libraries(tls_key.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME tls_key.gtest COMMAND tls_key.gtest) +install(TARGETS tls_key.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(signature.gtest signature_test.cpp) target_link_libraries(signature.gtest ${JOIN_CRYPTO} GTest::gtest_main) add_test(NAME signature.gtest COMMAND signature.gtest) install(TARGETS signature.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -# add_executable(tlserror.gtest tlserror_test.cpp) -# target_link_libraries(tlserror.gtest ${JOIN_CRYPTO} GTest::gtest_main) -# add_test(NAME tlserror.gtest COMMAND tlserror.gtest) -# install(TARGETS tlserror.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(tlscontext.gtest tlscontext_test.cpp) -target_link_libraries(tlscontext.gtest ${JOIN_CRYPTO} GTest::gtest_main) -add_test(NAME tlscontext.gtest COMMAND tlscontext.gtest) -install(TARGETS tlscontext.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(tlswrapper.gtest tlswrapper_test.cpp) -target_link_libraries(tlswrapper.gtest ${JOIN_CRYPTO} GTest::gtest_main) -add_test(NAME tlswrapper.gtest COMMAND tlswrapper.gtest) -install(TARGETS tlswrapper.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(dtlswrapper.gtest dtlswrapper_test.cpp) -target_link_libraries(dtlswrapper.gtest ${JOIN_CRYPTO} GTest::gtest_main) -add_test(NAME dtlswrapper.gtest COMMAND dtlswrapper.gtest) -install(TARGETS dtlswrapper.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(tls_error.gtest tls_error_test.cpp) +target_link_libraries(tls_error.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME tls_error.gtest COMMAND tls_error.gtest) +install(TARGETS tls_error.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(tls_context.gtest tls_context_test.cpp) +target_link_libraries(tls_context.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME tls_context.gtest COMMAND tls_context.gtest) +install(TARGETS tls_context.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(tls_protocol.gtest tls_protocol_test.cpp) +target_link_libraries(tls_protocol.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME tls_protocol.gtest COMMAND tls_protocol.gtest) +install(TARGETS tls_protocol.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(tls_wrapper.gtest tls_wrapper_test.cpp) +target_link_libraries(tls_wrapper.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME tls_wrapper.gtest COMMAND tls_wrapper.gtest) +install(TARGETS tls_wrapper.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(tls_stream.gtest tls_stream_test.cpp) +target_link_libraries(tls_stream.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME tls_stream.gtest COMMAND tls_stream.gtest) +install(TARGETS tls_stream.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(dtls_wrapper.gtest dtls_wrapper_test.cpp) +target_link_libraries(dtls_wrapper.gtest ${JOIN_CRYPTO} GTest::gtest_main) +add_test(NAME dtls_wrapper.gtest COMMAND dtls_wrapper.gtest) +install(TARGETS dtls_wrapper.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) diff --git a/crypto/tests/digesterror_test.cpp b/crypto/tests/digest_error_test.cpp similarity index 100% rename from crypto/tests/digesterror_test.cpp rename to crypto/tests/digest_error_test.cpp diff --git a/crypto/tests/dtlswrapper_test.cpp b/crypto/tests/dtls_wrapper_test.cpp similarity index 86% rename from crypto/tests/dtlswrapper_test.cpp rename to crypto/tests/dtls_wrapper_test.cpp index 8bb99079..ffc22c62 100644 --- a/crypto/tests/dtlswrapper_test.cpp +++ b/crypto/tests/dtls_wrapper_test.cpp @@ -23,9 +23,8 @@ */ // libjoin. -#include +#include #include -#include // Libraries. #include @@ -38,8 +37,8 @@ using join::IpAddress; using join::ReactorThread; using join::EventHandler; using join::Udp; +using join::Dtls; using join::TlsContext; -using join::TlsWrapper; /** * @brief Class used to test the datagram TLS wrapper API. @@ -230,7 +229,7 @@ class DtlsSocket : public EventHandler, public ::testing::Test TlsContext _tlsContext{TlsContext::Role::DtlsServer}; /// socket. - TlsWrapper _socket{_tlsContext, Udp::Socket::Blocking}; + Dtls::Socket _socket{_tlsContext, Udp::Socket::Blocking}; /// host. static const std::string _hostv4; @@ -274,7 +273,7 @@ const std::string DtlsSocket::_invalidKey = "/tmp/tlssocket_test_invalid.key"; TEST_F (DtlsSocket, move) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls1 (ctx, Udp::Socket::Blocking), dtls2 (ctx); + Dtls::Socket dtls1 (ctx, Udp::Socket::Blocking), dtls2 (ctx); ASSERT_EQ (dtls1.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); ASSERT_EQ (dtls1.connect ({_hostv4, _port}), 0) << join::lastError.message (); @@ -295,15 +294,15 @@ TEST_F (DtlsSocket, move) TEST_F (DtlsSocket, open) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx); + Dtls::Socket dtls (ctx); - ASSERT_EQ (dtls.open (Udp::v4 ()), 0) << join::lastError.message (); - ASSERT_EQ (dtls.open (Udp::v4 ()), -1); + ASSERT_EQ (dtls.open (Dtls::v4 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls::v4 ()), -1); ASSERT_EQ (join::lastError, Errc::InUse); dtls.close (); - ASSERT_EQ (dtls.open (Udp::v6 ()), 0) << join::lastError.message (); - ASSERT_EQ (dtls.open (Udp::v6 ()), -1); + ASSERT_EQ (dtls.open (Dtls::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls::v6 ()), -1); ASSERT_EQ (join::lastError, Errc::InUse); dtls.close (); } @@ -314,7 +313,7 @@ TEST_F (DtlsSocket, open) TEST_F (DtlsSocket, close) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_FALSE (dtls.opened ()); ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); @@ -331,7 +330,7 @@ TEST_F (DtlsSocket, close) TEST_F (DtlsSocket, bind) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), -1); @@ -350,7 +349,7 @@ TEST_F (DtlsSocket, bind) TEST_F (DtlsSocket, bindToDevice) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls.bindToDevice ("lo"), -1); @@ -359,7 +358,7 @@ TEST_F (DtlsSocket, bindToDevice) ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); dtls.close (); - ASSERT_EQ (dtls.open (Udp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls::v6 ()), 0) << join::lastError.message (); ASSERT_EQ (dtls.bindToDevice ("lo"), 0) << join::lastError.message (); ASSERT_EQ (dtls.connect ({_hostv6, _port}), 0) << join::lastError.message (); ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); @@ -374,7 +373,7 @@ TEST_F (DtlsSocket, bindToDevice) TEST_F (DtlsSocket, connect) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls.connect ({"255.255.255.255", _port}), -1); @@ -385,39 +384,17 @@ TEST_F (DtlsSocket, connect) dtls.close (); } -/** - * @brief Test waitConnected method. - */ -TEST_F (DtlsSocket, waitConnected) -{ - TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx); - - ASSERT_FALSE (dtls.waitConnected (_timeout)); - if (dtls.connect ({_hostv4, _port}) == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); - if (dtls.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); - dtls.close (); -} - /** * @brief Test handshake method. */ TEST_F (DtlsSocket, handshake) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls.handshake (), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (dtls.open (Udp::v4 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls::v4 ()), 0) << join::lastError.message (); ASSERT_EQ (dtls.handshake (), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); @@ -438,15 +415,14 @@ TEST_F (DtlsSocket, handshake) TEST_F (DtlsSocket, waitHandshake) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + Dtls::Socket dtls (ctx, Udp::Socket::NonBlocking); - ASSERT_EQ (dtls.open (Udp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls::v6 ()), 0) << join::lastError.message (); ASSERT_FALSE (dtls.waitHandshake (_timeout)); if (dtls.connect ({_hostv6, _port}) == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); } - ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); if (dtls.handshake () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -458,11 +434,7 @@ TEST_F (DtlsSocket, waitHandshake) ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); } ASSERT_TRUE (dtls.waitShutdown (_timeout)) << join::lastError.message (); - if (dtls.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); dtls.close (); } @@ -472,7 +444,7 @@ TEST_F (DtlsSocket, waitHandshake) TEST_F (DtlsSocket, disconnect) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_FALSE (dtls.connected ()); ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); @@ -483,36 +455,13 @@ TEST_F (DtlsSocket, disconnect) ASSERT_FALSE (dtls.connected ()); } -/** - * @brief Test waitDisconnected method. - */ -TEST_F (DtlsSocket, waitDisconnected) -{ - TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); - - ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); - if (dtls.connect ({_hostv4, _port}) == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); - ASSERT_FALSE (dtls.waitDisconnected (_timeout)); - if (dtls.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); - dtls.close (); -} - /** * @brief Test waitReadyRead method. */ TEST_F (DtlsSocket, waitReadyRead) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + Dtls::Socket dtls (ctx, Udp::Socket::NonBlocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_FALSE (dtls.waitReadyRead (_timeout)); @@ -522,7 +471,6 @@ TEST_F (DtlsSocket, waitReadyRead) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); } - ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); if (dtls.handshake () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -536,11 +484,7 @@ TEST_F (DtlsSocket, waitReadyRead) ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); } ASSERT_TRUE (dtls.waitShutdown (_timeout)) << join::lastError.message (); - if (dtls.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); dtls.close (); } @@ -550,7 +494,7 @@ TEST_F (DtlsSocket, waitReadyRead) TEST_F (DtlsSocket, read) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_EQ (dtls.read (data, sizeof (data)), -1); @@ -570,7 +514,7 @@ TEST_F (DtlsSocket, read) TEST_F (DtlsSocket, readExactly) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_EQ (dtls.readExactly (data, sizeof (data)), -1); @@ -593,7 +537,7 @@ TEST_F (DtlsSocket, readExactly) TEST_F (DtlsSocket, waitReadyWrite) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + Dtls::Socket dtls (ctx, Udp::Socket::NonBlocking); ASSERT_FALSE (dtls.waitReadyWrite (_timeout)); ASSERT_EQ (join::lastError, Errc::OperationFailed); @@ -602,7 +546,6 @@ TEST_F (DtlsSocket, waitReadyWrite) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); } - ASSERT_TRUE (dtls.waitConnected (_timeout)) << join::lastError.message (); if (dtls.handshake () == -1) { ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); @@ -614,11 +557,7 @@ TEST_F (DtlsSocket, waitReadyWrite) ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); } ASSERT_TRUE (dtls.waitShutdown (_timeout)) << join::lastError.message (); - if (dtls.disconnect () == -1) - { - ASSERT_EQ (join::lastError, Errc::TemporaryError) << join::lastError.message (); - } - ASSERT_TRUE (dtls.waitDisconnected (_timeout)) << join::lastError.message (); + ASSERT_EQ (dtls.disconnect (), 0) << join::lastError.message (); dtls.close (); } @@ -628,7 +567,7 @@ TEST_F (DtlsSocket, waitReadyWrite) TEST_F (DtlsSocket, write) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_EQ (dtls.write (data, sizeof (data)), -1); @@ -647,7 +586,7 @@ TEST_F (DtlsSocket, write) TEST_F (DtlsSocket, writeExactly) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_EQ (dtls.writeExactly (data, sizeof (data)), -1); @@ -669,7 +608,7 @@ TEST_F (DtlsSocket, writeExactly) TEST_F (DtlsSocket, setMode) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + Dtls::Socket dtls (ctx, Udp::Socket::NonBlocking); ASSERT_EQ (dtls.open (), 0) << join::lastError.message (); @@ -693,7 +632,7 @@ TEST_F (DtlsSocket, setMode) TEST_F (DtlsSocket, setOption) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + Dtls::Socket dtls (ctx, Udp::Socket::NonBlocking); ASSERT_EQ (dtls.setOption (Udp::Socket::RcvBuffer, 1500), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); @@ -723,7 +662,7 @@ TEST_F (DtlsSocket, setOption) ASSERT_EQ (join::lastError, std::errc::no_protocol_option); dtls.close (); - ASSERT_EQ (dtls.open (Udp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls::v6 ()), 0) << join::lastError.message (); ASSERT_EQ (dtls.setOption (Udp::Socket::NoDelay, 1), -1); ASSERT_EQ (join::lastError, Errc::InvalidParam); ASSERT_EQ (dtls.setOption (Udp::Socket::KeepAlive, 1), 0) << join::lastError.message (); @@ -755,7 +694,7 @@ TEST_F (DtlsSocket, setOption) TEST_F (DtlsSocket, localEndpoint) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls.localEndpoint (), Udp::Endpoint{}); ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); @@ -770,7 +709,7 @@ TEST_F (DtlsSocket, localEndpoint) TEST_F (DtlsSocket, remoteEndpoint) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls.remoteEndpoint (), Udp::Endpoint{}); ASSERT_EQ (dtls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); @@ -785,10 +724,10 @@ TEST_F (DtlsSocket, remoteEndpoint) TEST_F (DtlsSocket, opened) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_FALSE (dtls.opened ()); - ASSERT_EQ (dtls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); ASSERT_TRUE (dtls.opened ()); ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); @@ -806,10 +745,10 @@ TEST_F (DtlsSocket, opened) TEST_F (DtlsSocket, connected) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_FALSE (dtls.connected ()); - ASSERT_EQ (dtls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); ASSERT_FALSE (dtls.connected ()); ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); @@ -827,10 +766,10 @@ TEST_F (DtlsSocket, connected) TEST_F (DtlsSocket, encrypted) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_FALSE (dtls.encrypted ()); - ASSERT_EQ (dtls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); ASSERT_FALSE (dtls.encrypted ()); ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_FALSE (dtls.encrypted ()); @@ -850,7 +789,7 @@ TEST_F (DtlsSocket, encrypted) TEST_F (DtlsSocket, family) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls.family (), AF_INET); @@ -869,7 +808,7 @@ TEST_F (DtlsSocket, family) TEST_F (DtlsSocket, type) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + Dtls::Socket dtls (ctx, Udp::Socket::NonBlocking); ASSERT_EQ (dtls.type (), SOCK_DGRAM); } @@ -880,7 +819,7 @@ TEST_F (DtlsSocket, type) TEST_F (DtlsSocket, protocol) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::NonBlocking); + Dtls::Socket dtls (ctx, Udp::Socket::NonBlocking); ASSERT_EQ (dtls.protocol (), IPPROTO_UDP); } @@ -891,10 +830,10 @@ TEST_F (DtlsSocket, protocol) TEST_F (DtlsSocket, handle) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls.handle (), -1); - ASSERT_EQ (dtls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls.open (Dtls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); ASSERT_GT (dtls.handle (), -1); ASSERT_EQ (dtls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (dtls.handshake (), 0) << join::lastError.message (); @@ -912,7 +851,7 @@ TEST_F (DtlsSocket, handle) TEST_F (DtlsSocket, mtu) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls.mtu (), -1); ASSERT_EQ (dtls.connect ({"127.0.0.1", _port}), 0) << join::lastError.message (); @@ -943,7 +882,7 @@ TEST_F (DtlsSocket, verify) Udp::Endpoint dest{_hostv4, _port}; ctx.setVerify (false); - TlsWrapper dtls1 (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls1 (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls1.bind (src), 0) << join::lastError.message (); ASSERT_EQ (dtls1.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls1.handshake (), 0) << join::lastError.message (); @@ -952,7 +891,7 @@ TEST_F (DtlsSocket, verify) dtls1.close (); ctx.setVerify (true, 0); - TlsWrapper dtls2 (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls2 (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls2.bind (src), 0) << join::lastError.message (); ASSERT_EQ (dtls2.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls2.handshake (), -1); @@ -960,7 +899,7 @@ TEST_F (DtlsSocket, verify) dtls2.close (); ctx.setVerify (true, 1); - TlsWrapper dtls3 (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls3 (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls3.bind (src), 0) << join::lastError.message (); ASSERT_EQ (dtls3.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls3.handshake (), -1); @@ -970,7 +909,7 @@ TEST_F (DtlsSocket, verify) dest.hostname ("localhost"); ctx.setVerify (true, 0); - TlsWrapper dtls4 (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls4 (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls4.bind (src), 0) << join::lastError.message (); ASSERT_EQ (dtls4.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls4.handshake (), -1); @@ -978,7 +917,7 @@ TEST_F (DtlsSocket, verify) dtls4.close (); ctx.setVerify (true, 1); - TlsWrapper dtls5 (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls5 (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls5.bind (src), 0) << join::lastError.message (); ASSERT_EQ (dtls5.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls5.handshake (), -1); @@ -988,7 +927,7 @@ TEST_F (DtlsSocket, verify) ASSERT_EQ (ctx.setCaFile (_rootcert), 0) << join::lastError.message (); ctx.setVerify (true, 0); - TlsWrapper dtls6 (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls6 (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls6.bind (src), 0) << join::lastError.message (); ASSERT_EQ (dtls6.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls6.handshake (), -1); @@ -996,7 +935,7 @@ TEST_F (DtlsSocket, verify) dtls6.close (); ctx.setVerify (true, 1); - TlsWrapper dtls7 (ctx, Udp::Socket::Blocking); + Dtls::Socket dtls7 (ctx, Udp::Socket::Blocking); ASSERT_EQ (dtls7.bind (src), 0) << join::lastError.message (); ASSERT_EQ (dtls7.connect (dest), 0) << join::lastError.message (); ASSERT_EQ (dtls7.handshake (), 0) << join::lastError.message (); @@ -1011,10 +950,10 @@ TEST_F (DtlsSocket, verify) TEST_F (DtlsSocket, isLower) { TlsContext ctx (TlsContext::Role::DtlsClient); - TlsWrapper dtls1 (ctx), dtls2 (ctx); + Dtls::Socket dtls1 (ctx), dtls2 (ctx); - ASSERT_EQ (dtls1.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - ASSERT_EQ (dtls2.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (dtls1.open (Dtls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); + ASSERT_EQ (dtls2.open (Dtls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); if (dtls1.handle () < dtls2.handle ()) { ASSERT_TRUE (dtls1 < dtls2); diff --git a/crypto/tests/tlscontext_test.cpp b/crypto/tests/tls_context_test.cpp similarity index 99% rename from crypto/tests/tlscontext_test.cpp rename to crypto/tests/tls_context_test.cpp index fdfe2652..31319692 100644 --- a/crypto/tests/tlscontext_test.cpp +++ b/crypto/tests/tls_context_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include // Libraries. diff --git a/crypto/tests/tlserror_test.cpp b/crypto/tests/tls_error_test.cpp similarity index 99% rename from crypto/tests/tlserror_test.cpp rename to crypto/tests/tls_error_test.cpp index 3d5ac2a4..2ea65d92 100644 --- a/crypto/tests/tlserror_test.cpp +++ b/crypto/tests/tls_error_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/crypto/tests/tlskey_test.cpp b/crypto/tests/tls_key_test.cpp similarity index 99% rename from crypto/tests/tlskey_test.cpp rename to crypto/tests/tls_key_test.cpp index df0bc6ae..7daf9249 100644 --- a/crypto/tests/tlskey_test.cpp +++ b/crypto/tests/tls_key_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/crypto/tests/tls_protocol_test.cpp b/crypto/tests/tls_protocol_test.cpp new file mode 100644 index 00000000..6c63fd89 --- /dev/null +++ b/crypto/tests/tls_protocol_test.cpp @@ -0,0 +1,88 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include + +// Libraries. +#include + +using join::Dtls; +using join::Tls; + +/** + * @brief test the family method. + */ +TEST (TlsProtocol, family) +{ + ASSERT_EQ (Dtls ().family (), AF_INET); + ASSERT_EQ (Dtls::v6 ().family (), AF_INET6); + ASSERT_EQ (Dtls::v4 ().family (), AF_INET); + ASSERT_EQ (Tls ().family (), AF_INET); + ASSERT_EQ (Tls::v6 ().family (), AF_INET6); + ASSERT_EQ (Tls::v4 ().family (), AF_INET); +} + +/** + * @brief test the type method. + */ +TEST (TlsProtocol, type) +{ + ASSERT_EQ (Dtls ().type (), SOCK_DGRAM); + ASSERT_EQ (Tls ().type (), SOCK_STREAM); +} + +/** + * @brief test the protocol method. + */ +TEST (TlsProtocol, protocol) +{ + ASSERT_EQ (Dtls ().protocol (), IPPROTO_UDP); + ASSERT_EQ (Tls ().protocol (), IPPROTO_TCP); +} + +/** + * @brief equal method. + */ +TEST (TlsProtocol, equal) +{ + ASSERT_EQ (Dtls::v4 (), Dtls::v4 ()); + ASSERT_NE (Dtls::v4 (), Dtls::v6 ()); + ASSERT_EQ (Dtls::v6 (), Dtls::v6 ()); + ASSERT_NE (Dtls::v6 (), Dtls::v4 ()); + + ASSERT_EQ (Tls::v4 (), Tls::v4 ()); + ASSERT_NE (Tls::v4 (), Tls::v6 ()); + ASSERT_EQ (Tls::v6 (), Tls::v6 ()); + ASSERT_NE (Tls::v6 (), Tls::v4 ()); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/crypto/tests/tls_stream_test.cpp b/crypto/tests/tls_stream_test.cpp new file mode 100644 index 00000000..966503ed --- /dev/null +++ b/crypto/tests/tls_stream_test.cpp @@ -0,0 +1,837 @@ +/** + * MIT License + * + * Copyright (c) 2021 Mathieu Rabine + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +// libjoin. +#include +#include +#include + +// Libraries. +#include + +// C++. +#include + +using join::Errc; +using join::IpAddress; +using join::ReactorThread; +using join::EventHandler; +using join::Tcp; +using join::Tls; +using join::TlsContext; + +/** + * @brief Class used to test the TLS socket stream API. + */ +class TlsSocketStream : public EventHandler, public ::testing::Test +{ +public: + /** + * @brief set up test case. + */ + static void SetUpTestCase () + { + std::ofstream rootCertFile (_rootcert); + if (rootCertFile.is_open ()) + { + rootCertFile << "-----BEGIN CERTIFICATE-----" << std::endl; + rootCertFile << "MIIChjCCAisCFBuHxbqMUGyl7OQUQcoRg3pOBJF+MAoGCCqGSM49BAMCMIHEMQsw" << std::endl; + rootCertFile << "CQYDVQQGEwJGUjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVz" << std::endl; + rootCertFile << "MRcwFQYDVQQKDA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdv" << std::endl; + rootCertFile << "cmsgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3" << std::endl; + rootCertFile << "b3JrLm5ldDEoMCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5l" << std::endl; + rootCertFile << "dDAeFw0yMjA3MDUxNjMxMTZaFw0zMjA3MDIxNjMxMTZaMIHEMQswCQYDVQQGEwJG" << std::endl; + rootCertFile << "UjESMBAGA1UECAwJT2NjaXRhbmllMRAwDgYDVQQHDAdDYXN0cmVzMRcwFQYDVQQK" << std::endl; + rootCertFile << "DA5Kb2luIEZyYW1ld29yazEtMCsGA1UECwwkSm9pbiBGcmFtZXdvcmsgQ2VydGlm" << std::endl; + rootCertFile << "aWNhdGUgQXV0aG9yaXR5MR0wGwYDVQQDDBRjYS5qb2luZnJhbWV3b3JrLm5ldDEo" << std::endl; + rootCertFile << "MCYGCSqGSIb3DQEJARYZc3VwcG9ydEBqb2luZnJhbWV3b3JrLm5ldDBZMBMGByqG" << std::endl; + rootCertFile << "SM49AgEGCCqGSM49AwEHA0IABASk0zCrKtXQi0Ycx+Anx+VWv8gncbPmNQ1yutii" << std::endl; + rootCertFile << "gQjP2mF9NIqlxpcKNuE/6DDnfSzCEDhFyvGiK0NJ1C3RBowwCgYIKoZIzj0EAwID" << std::endl; + rootCertFile << "SQAwRgIhAIFqdbxTb5kRjy4UY0N205ZEhHSMK89p2oUyn4iNbXH2AiEAtmV1UyRX" << std::endl; + rootCertFile << "DIAGr/F+1SwQMPoJzSQxZ7NdxjNgW286e9Q=" << std::endl; + rootCertFile << "-----END CERTIFICATE-----" << std::endl; + rootCertFile.close (); + } + + mkdir (_certPath.c_str (), 0777); + std::ofstream certFile (_certFile); + if (certFile.is_open ()) + { + certFile << "-----BEGIN CERTIFICATE-----" << std::endl; + certFile << "MIIDgDCCAyagAwIBAgIUR3ZIuKMt0BdaOZQnPwhSMR9qzfgwCgYIKoZIzj0EAwIw" << std::endl; + certFile << "gcQxCzAJBgNVBAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nh" << std::endl; + certFile << "c3RyZXMxFzAVBgNVBAoMDkpvaW4gRnJhbWV3b3JrMS0wKwYDVQQLDCRKb2luIEZy" << std::endl; + certFile << "YW1ld29yayBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHTAbBgNVBAMMFGNhLmpvaW5m" << std::endl; + certFile << "cmFtZXdvcmsubmV0MSgwJgYJKoZIhvcNAQkBFhlzdXBwb3J0QGpvaW5mcmFtZXdv" << std::endl; + certFile << "cmsubmV0MB4XDTIyMDcwNzEyMTIxMFoXDTMyMDcwNDEyMTIxMFowgagxCzAJBgNV" << std::endl; + certFile << "BAYTAkZSMRIwEAYDVQQIDAlPY2NpdGFuaWUxEDAOBgNVBAcMB0Nhc3RyZXMxFzAV" << std::endl; + certFile << "BgNVBAoMDkpvaW4gRnJhbWV3b3JrMRswGQYDVQQLDBJKb2luIEZyYW1ld29yayBE" << std::endl; + certFile << "ZXYxEzARBgNVBAMMCmxvY2FsaG9zdC4xKDAmBgkqhkiG9w0BCQEWGXN1cHBvcnRA" << std::endl; + certFile << "am9pbmZyYW1ld29yay5uZXQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB" << std::endl; + certFile << "AQDSNtw5zEoJFPf6Rl0Y1n8BQfE0YTPCELvFAeioUfj8CAnUleHL9pwAEFg6kgoG" << std::endl; + certFile << "hvwto5/yWGPUqNNfe3xbFTJcHgMhgtjqy5H6sYDkTi3kYIIMBfTHr8NI7HWE8Nz1" << std::endl; + certFile << "qU1snjtERnkoLilIZf/2BojNVMtHC1H316WbMicXS0v7HQo3lv6PYSana9Q9ow9O" << std::endl; + certFile << "2/FiW5qq1eOhI1ZedRanX+bl0jHWCd3WsI87+5bTaQrfetdHTOmav6O17Iq9FiTh" << std::endl; + certFile << "Sg9fbM3s2Hw15kI+mws029dhcwXs5sYY+NgtrQwjR5qH+54BdUaPwQfl/KyulfEl" << std::endl; + certFile << "TJykJ+3w6MorxUr55F68uBNbAgMBAAGjRTBDMAsGA1UdDwQEAwIF4DAdBgNVHSUE" << std::endl; + certFile << "FjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwFQYDVR0RBA4wDIIKbG9jYWxob3N0LjAK" << std::endl; + certFile << "BggqhkjOPQQDAgNIADBFAiA120ufIbhcw7BJQ1L6WudDdW2mHrVXvdgeOzVGgz1d" << std::endl; + certFile << "iAIhAMm/sWI3yzb2IMPffxWKYusWEQE2hZvs24ESSC/ZZ0s+" << std::endl; + certFile << "-----END CERTIFICATE-----" << std::endl; + certFile.close (); + } + + [[maybe_unused]] int result; + result = std::system (std::string ("/usr/bin/c_rehash " + _certPath).c_str ()); + + std::ofstream keyFile (_key); + if (keyFile.is_open ()) + { + keyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; + keyFile << "MIIEowIBAAKCAQEA0jbcOcxKCRT3+kZdGNZ/AUHxNGEzwhC7xQHoqFH4/AgJ1JXh" << std::endl; + keyFile << "y/acABBYOpIKBob8LaOf8lhj1KjTX3t8WxUyXB4DIYLY6suR+rGA5E4t5GCCDAX0" << std::endl; + keyFile << "x6/DSOx1hPDc9alNbJ47REZ5KC4pSGX/9gaIzVTLRwtR99elmzInF0tL+x0KN5b+" << std::endl; + keyFile << "j2Emp2vUPaMPTtvxYluaqtXjoSNWXnUWp1/m5dIx1gnd1rCPO/uW02kK33rXR0zp" << std::endl; + keyFile << "mr+jteyKvRYk4UoPX2zN7Nh8NeZCPpsLNNvXYXMF7ObGGPjYLa0MI0eah/ueAXVG" << std::endl; + keyFile << "j8EH5fysrpXxJUycpCft8OjKK8VK+eRevLgTWwIDAQABAoIBAAzdlK7o5OMXaHHl" << std::endl; + keyFile << "2o7Jme5Oxd9pz4wiEAvnqQCcO7vZFhjvr2kXR8btOSkkhP6PRmHYsNJZPIroZj9i" << std::endl; + keyFile << "xGKisnlW0OQ9KN995ApO0M+oRUDD81GfD7Mk+7O73Rls0GksmnN6X7A3C/U8lgQ7" << std::endl; + keyFile << "UeYR0k+Wz/YiKDsd9KHB+QiA8D6HFQ9I8Y2P97KOcYnxXZfSwNm+ENNU3wShZOl2" << std::endl; + keyFile << "ZYJJ4DE+5m2SwZ6g8b5Zre4cDbOduwuz/jXzjy2tAZBlTS4DVpYlhd14z+ssUWiu" << std::endl; + keyFile << "AdS/nqSF7Obj0TRhoGNfrkisFzV4itavQ5DKGj/6hjueIJVLteUOzcCeg26YosNy" << std::endl; + keyFile << "QzZSjOECgYEA7y3InEoh93/4HZCZmdwN8KfZtqirX0t966FntgAT8RkIs+KvNS8B" << std::endl; + keyFile << "m3RfNLa/EuDt5zTmHRGx+oeN+17i9QQjKWcR0NnJ6aSZbvJByj3yKxLF9XVllzp/" << std::endl; + keyFile << "vHSSyB264RoKIrWmFN6cCO4u4h9ZPY75pASWBCDMdnGK8axAcqAnlqsCgYEA4P+Y" << std::endl; + keyFile << "FF9RW4rhrVU4dpXSfcr6vOwqfp9F9vhTVL0JS/SLOFoJNNpS9Rnq3pVLEuKyCphd" << std::endl; + keyFile << "3nk9VFfoRygmMaGBvwGaXZPPvosoaIUgOdTt7KIfSHPichBEVxRuWCrtTGGkG0ok" << std::endl; + keyFile << "s/RPHhvxZE267vsVj1PktK8Yr5Ba0AL2ycztNhECgYB5OAwHYe8LIBlg6otelk+e" << std::endl; + keyFile << "W4OU9rE8L+eWx4vniuyQce6eNNI1syguYHFsJv56E/OfDYlezDwWzCLidnmyUjF7" << std::endl; + keyFile << "51f5MJgLyTdWKoO7e1/EAtS/jYs6dRSOL8rAj4jKU0c1xjhxNU2BnS23vsmc0Fyn" << std::endl; + keyFile << "iwd4+iKGGQ+hYnqbXZ4S1wKBgD/3an0gPDkSWua0e8D7B0TMGEztt4cYMQPtxYMp" << std::endl; + keyFile << "2yLE+2+h6UwlZcBZBfUR7K4J1SQ9/THqtgzskRTpzTH/AKwVAJXqF/3MAkj00Byg" << std::endl; + keyFile << "9KN50/r9NzvGdCdtn5FhYuV8PPOlOJoQsw2UVCR4FNUsfQyqhTL5NMN0/tx0e0UU" << std::endl; + keyFile << "BbyBAoGBANu5ifByauVELH8UEl5rXRu1S9iAVV+Bc5jboXwc4VxJtEyomGJ7+YdL" << std::endl; + keyFile << "5c9LFV+STUp7CE12uSXQZTQM0tEjPinLntRinNzu9tIHR1vy7FZHEwMFIgB4VTY7" << std::endl; + keyFile << "ALRYv1/QpTuywpNUFRS15JkfGNf5JIkrUEWLgkX3OVCBsRGHUugy" << std::endl; + keyFile << "-----END RSA PRIVATE KEY-----" << std::endl; + keyFile.close (); + } + + std::ofstream invalidKeyFile (_invalidKey); + if (invalidKeyFile.is_open ()) + { + invalidKeyFile << "-----BEGIN RSA PRIVATE KEY-----" << std::endl; + invalidKeyFile << "MIIEowIBAAKCAQEA2Q0DOyG039uVMuxNnZ5fpfOcvXXOTguST1QR6eLVkdG7OKpM" << std::endl; + invalidKeyFile << "nc9K597jx1syT1q+SwFcykMtvWxCfD8BR7bcLILeO6z+HlRfvjOhUiHaX/KCaTN8" << std::endl; + invalidKeyFile << "l7OJOgmUlL0FhQ1SXxw7KCSGd+rgu1iHwjFDDkj/tG24ashdmNt+DYdeoJu2mzgw" << std::endl; + invalidKeyFile << "tEASfG9VjqBR7ni4Hg/sRpwXvEK5nI1JSLyZbcPCxGlBRdB8hMdny/VW+SBwKD2/" << std::endl; + invalidKeyFile << "ivpVJLulw2oniSIcCCcr9d+ERY4XrO71UsiACwPxfdEtbG0KrZfpK91k7vl64DHM" << std::endl; + invalidKeyFile << "CeTQPKRZm+LDKOUfv/eTF9F6GY4Dpw2LMwLM5QIDAQABAoIBABjV91etzK+Mxa61" << std::endl; + invalidKeyFile << "AVCWzaUEkhvPvhKKGmy/VulnTj7IO98JBYlNLeoIRBIMql4QKRQWDNMMCtDQ8W6c" << std::endl; + invalidKeyFile << "Gv5kux7QvrMfYViBGQ9/gucN/pnZ+vgkrw4AuiQM8pZuZpJJ6vH9HfvC6iwQkTR+" << std::endl; + invalidKeyFile << "tdIPpvecfL3djCuTz7ns66iKo9ZGpRE6emTBynr8og/oqD8Vw5bW+JJ+AJ3IqZf4" << std::endl; + invalidKeyFile << "NslNist7d5FZ5N/+nxWyBUcFglP7bZzb/raOVc/flrYIeDy72asnWOYbDTPzMyH1" << std::endl; + invalidKeyFile << "dfaox6QKZtA5NdO9x4aHHGgAz8BTgqs7LvxPwoH+XF1dDCsb3kIeQxHTfcc1opMw" << std::endl; + invalidKeyFile << "atxpgwECgYEA8Zq/7Z3tKcBlMz4XNKWWvaDxhBUIS62tGeLJ2spLRFvkL1ixnjcK" << std::endl; + invalidKeyFile << "72YWOwDpoINEWa8AhAhM6afE9VxrupSGg+C9uALaJ8HTWTP6u6/F8sbsYaoWHyA/" << std::endl; + invalidKeyFile << "k/8/nFEr43ciKUjBhMHB42vYidAgiOvDVXc+/k7HIMQfl/vyp32ecEECgYEA5fu9" << std::endl; + invalidKeyFile << "ePLh55TYbXe8SCL0hsZcC8Q/ioT/0GJ6uevGb0lw3XAa+HC6//upu90T7ZOIqysc" << std::endl; + invalidKeyFile << "aAqln7ZEeCfvXI/3YJyJ2RWatD+2itECbd0WV2/JflO/OAzDSSFvpxxmwIzccIeA" << std::endl; + invalidKeyFile << "UNuNcQGD8HDwFzU+sULvF82yuwMt1syPd/mns6UCgYAviqP5vfnNHW7MhotKcMsY" << std::endl; + invalidKeyFile << "xXLA6uKXAbXuQhI2W1g0O2DLcEiDOZGNSilVsvhF/Y6VlzoiwP9hewHmxijsrg1K" << std::endl; + invalidKeyFile << "Jg8vBmCnMhzEkNXl2NC61SnujemMdmwMU03RFKfuOqMePJLX7MiaV75kX/AHAV2O" << std::endl; + invalidKeyFile << "k8hxgk7sw6rz3UACdVWYAQKBgHUu5ScoksS+Cd0VQmF7Nh8qGSKBt2KsS/BxDVmI" << std::endl; + invalidKeyFile << "ck6oHBMomQV340CliaHIjuvh3aRhzhKRQjzz0UVsC8GdNY4LlQ2AvZgUUr2+q78x" << std::endl; + invalidKeyFile << "BL4+nmt43pj/n822dL6wcQaxf2zzDgWlKReojwLHeP5KSgxmL49wZx51CzlEd+HI" << std::endl; + invalidKeyFile << "2pNlAoGBAObdC7woN7jEfdfYz1BhUpmBsIRqW2yLA1DnlK9lfgs2i1w7spzAh2hV" << std::endl; + invalidKeyFile << "djPiKj5vZdcrbaa+SBAnZbFTHyXmAbKbO/iZpSromaZYyCK8NktJu/YxpWZmjnRF" << std::endl; + invalidKeyFile << "2xOadRGCav5fTGzCN/ADLgIo4gIAI2o/UnV/MdaSAdHyIeSrxBAb" << std::endl; + invalidKeyFile << "-----END RSA PRIVATE KEY-----" << std::endl; + invalidKeyFile.close (); + } + } + + /** + * @brief tear down test case. + */ + static void TearDownTestCase () + { + unlink (_rootcert.c_str ()); + unlink (_certFile.c_str ()); + rmdir (_certPath.c_str ()); + unlink (_key.c_str ()); + unlink (_invalidKey.c_str ()); + } + +protected: + /** + * @brief Sets up the test fixture. + */ + void SetUp () override + { + ASSERT_EQ (_tlsContext.setCertificate (_certFile, _key), 0); + ASSERT_EQ (_tlsContext.setCipher (join::defaultCipher), 0); + ASSERT_EQ (_tlsContext.setCipher_1_3 (join::defaultCipher_1_3), 0); + + ASSERT_EQ (_acceptor.create ({IpAddress::ipv6Wildcard, _port}), 0); + ASSERT_EQ (ReactorThread::reactor ().addHandler (_acceptor.handle (), this), 0); + } + + /** + * @brief Tears down the test fixture. + */ + void TearDown () override + { + ASSERT_EQ (ReactorThread::reactor ().delHandler (_acceptor.handle ()), 0) << join::lastError.message (); + _acceptor.close (); + } + + /** + * @brief method called when data are ready to be read on handle. + * @param fd file descriptor. + */ + virtual void onReadable ([[maybe_unused]] int fd) override + { + Tls::Stream stream{_acceptor.accept (), _tlsContext}; + stream.handshake (); + if (stream.encrypted ()) + { + char buf[1024]; + for (;;) + { + // echo received data. + int nread = stream.socket ().read (buf, sizeof (buf)); + if (nread == -1) + { + if (join::lastError == Errc::TemporaryError) + { + if (stream.socket ().waitReadyRead (_timeout)) + continue; + } + break; + } + stream.socket ().writeExactly (buf, nread); + } + stream.shutdown (); + stream.close (); + } + } + + /// TLS context serveur. + TlsContext _tlsContext{TlsContext::Role::TlsServer}; + + /// server. + Tcp::Acceptor _acceptor; + + /// host. + static const std::string _host; + + /// port. + static const uint16_t _port; + static const uint16_t _invalid_port; + + /// timeout. + static const int _timeout; + + /// root certificate. + static const std::string _rootcert; + + /// certificate path. + static const std::string _certPath; + + /// certificate file. + static const std::string _certFile; + + /// private key. + static const std::string _key; + + /// invalid private key. + static const std::string _invalidKey; +}; + +const std::string TlsSocketStream::_host = "127.0.0.1"; +const uint16_t TlsSocketStream::_port = 5000; +const uint16_t TlsSocketStream::_invalid_port = 5032; +const int TlsSocketStream::_timeout = 1000; +const std::string TlsSocketStream::_rootcert = "/tmp/tlssocket_test_root.cert"; +const std::string TlsSocketStream::_certPath = "/tmp/certs"; +const std::string TlsSocketStream::_certFile = _certPath + "/tlssocket_test.cert"; +const std::string TlsSocketStream::_key = "/tmp/tlssocket_test.key"; +const std::string TlsSocketStream::_invalidKey = "/tmp/tlssocket_test_invalid.key"; + +/** + * @brief Test default constructor. + */ +TEST_F (TlsSocketStream, defaultConstruct) +{ + Tls::Stream tlsStream; + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); +} + +/** + * @brief Test move constructor. + */ +TEST_F (TlsSocketStream, moveConstruct) +{ + Tls::Stream tmp; + ASSERT_TRUE (tmp.good ()) << join::lastError.message (); + Tls::Stream tlsStream (std::move (tmp)); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); +} + +/** + * @brief Test move operator. + */ +TEST_F (TlsSocketStream, moveAssign) +{ + Tls::Stream tmp; + ASSERT_TRUE (tmp.good ()) << join::lastError.message (); + Tls::Stream tlsStream; + tlsStream = std::move (tmp); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); +} + +/** + * @brief Test bind method. + */ +TEST_F (TlsSocketStream, bind) +{ + Tls::Stream tlsStream; + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.bind (_host); + ASSERT_TRUE (tlsStream.fail ()); + tlsStream.clear (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.bind (_host); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test connect method. + */ +TEST_F (TlsSocketStream, connect) +{ + Tls::Stream tlsStream; + tlsStream.connect ({"255.255.255.255", _port}); + ASSERT_TRUE (tlsStream.fail ()); + tlsStream.clear (); + tlsStream.connect ({_host, _invalid_port}); + ASSERT_TRUE (tlsStream.fail ()); + tlsStream.clear (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.fail ()); + ASSERT_EQ (join::lastError, Errc::InUse); + tlsStream.clear (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test handshake method. + */ +TEST_F (TlsSocketStream, handshake) +{ + Tls::Stream tlsStream; + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.fail ()); + ASSERT_EQ (join::lastError, Errc::OperationFailed); + tlsStream.clear (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_TRUE (tlsStream.encrypted ()); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test shutdown method. + */ +TEST_F (TlsSocketStream, shutdown) +{ + Tls::Stream tlsStream; + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_TRUE (tlsStream.encrypted ()); + tlsStream.shutdown (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_FALSE (tlsStream.encrypted ()); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test disconnect method. + */ +TEST_F (TlsSocketStream, disconnect) +{ + Tls::Stream tlsStream; + ASSERT_FALSE (tlsStream.connected ()); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_TRUE (tlsStream.connected ()); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_FALSE (tlsStream.connected ()); + tlsStream.close (); + ASSERT_FALSE (tlsStream.connected ()); +} + +/** + * @brief Test close method. + */ +TEST_F (TlsSocketStream, close) +{ + Tls::Stream tlsStream; + ASSERT_FALSE (tlsStream.opened ()); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_TRUE (tlsStream.opened ()); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_TRUE (tlsStream.opened ()); + tlsStream.close (); + ASSERT_FALSE (tlsStream.opened ()); +} + +/** + * @brief Test localEndpoint method. + */ +TEST_F (TlsSocketStream, localEndpoint) +{ + Tls::Stream tlsStream; + ASSERT_EQ (tlsStream.localEndpoint (), Tls::Endpoint{}); + tlsStream.bind ({_host, uint16_t (_port + 1)}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_EQ (tlsStream.localEndpoint (), Tls::Endpoint (_host, uint16_t (_port + 1))) << join::lastError.message (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test remoteEndpoint method. + */ +TEST_F (TlsSocketStream, remoteEndpoint) +{ + Tls::Stream tlsStream; + ASSERT_EQ (tlsStream.remoteEndpoint (), Tls::Endpoint{}); + tlsStream.bind ({_host, uint16_t (_port + 1)}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_EQ (tlsStream.remoteEndpoint (), Tls::Endpoint (_host, _port)) << join::lastError.message (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test opened method. + */ +TEST_F (TlsSocketStream, opened) +{ + Tls::Stream tlsStream; + ASSERT_FALSE (tlsStream.opened ()); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_TRUE (tlsStream.opened ()); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_TRUE (tlsStream.opened ()); + tlsStream.close (); + ASSERT_FALSE (tlsStream.opened ()); +} + +/** + * @brief Test connected method. + */ +TEST_F (TlsSocketStream, connected) +{ + Tls::Stream tlsStream; + ASSERT_FALSE (tlsStream.connected ()); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_TRUE (tlsStream.connected ()); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_FALSE (tlsStream.connected ()); + tlsStream.close (); + ASSERT_FALSE (tlsStream.connected ()); +} + +/** + * @brief Test encrypted method. + */ +TEST_F (TlsSocketStream, encrypted) +{ + Tls::Stream tlsStream; + ASSERT_FALSE (tlsStream.encrypted ()); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_FALSE (tlsStream.encrypted ()); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_TRUE (tlsStream.encrypted ()); + tlsStream.shutdown (); + ASSERT_FALSE (tlsStream.encrypted ()); + tlsStream.disconnect (); + ASSERT_FALSE (tlsStream.encrypted ()); + tlsStream.close (); + ASSERT_FALSE (tlsStream.encrypted ()); +} + +/** + * @brief Test timeout method. + */ +TEST_F (TlsSocketStream, timeout) +{ + Tls::Stream tlsStream; + ASSERT_NE (tlsStream.timeout (), _timeout); + tlsStream.timeout (_timeout); + ASSERT_EQ (tlsStream.timeout (), _timeout); +} + +/** + * @brief Test socket method. + */ +TEST_F (TlsSocketStream, socket) +{ + Tls::Stream tlsStream; + ASSERT_EQ (tlsStream.socket ().handle (), -1); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_NE (tlsStream.socket ().handle (), -1); + tlsStream.close (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + ASSERT_EQ (tlsStream.socket ().handle (), -1); +} + +/** + * @brief Test insert operator. + */ +TEST_F (TlsSocketStream, insert) +{ + Tls::Stream tlsStream; + tlsStream << "test" << std::endl; + ASSERT_TRUE (tlsStream.fail ()); + ASSERT_EQ (join::lastError, Errc::ConnectionClosed); + tlsStream.clear (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream << "test" << std::endl; + tlsStream.flush (); + ASSERT_TRUE (tlsStream.socket ().waitReadyRead (_timeout)); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test put method. + */ +TEST_F (TlsSocketStream, put) +{ + Tls::Stream tlsStream; + tlsStream.put ('t'); + ASSERT_TRUE (tlsStream.fail ()); + ASSERT_EQ (join::lastError, Errc::ConnectionClosed); + tlsStream.clear (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.put ('t'); + tlsStream.put ('e'); + tlsStream.put ('s'); + tlsStream.put ('t'); + tlsStream.flush (); + ASSERT_TRUE (tlsStream.socket ().waitReadyRead (_timeout)); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test write method. + */ +TEST_F (TlsSocketStream, write) +{ + Tls::Stream tlsStream; + tlsStream.write ("test", 4); + ASSERT_TRUE (tlsStream.fail ()); + ASSERT_EQ (join::lastError, Errc::ConnectionClosed); + tlsStream.clear (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.write ("test", 4); + tlsStream.flush (); + ASSERT_TRUE (tlsStream.socket ().waitReadyRead (_timeout)); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test flush method. + */ +TEST_F (TlsSocketStream, flush) +{ + Tls::Stream tlsStream; + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.put ('t'); + tlsStream.flush (); + tlsStream.put ('e'); + tlsStream.flush (); + tlsStream.put ('s'); + tlsStream.flush (); + tlsStream.put ('t'); + tlsStream.flush (); + ASSERT_TRUE (tlsStream.socket ().waitReadyRead (_timeout)); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test extract method. + */ +TEST_F (TlsSocketStream, extract) +{ + int test; + Tls::Stream tlsStream; + tlsStream >> test; + ASSERT_TRUE (tlsStream.fail ()); + ASSERT_EQ (join::lastError, Errc::ConnectionClosed); + tlsStream.clear (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream << int (123456789) << std::endl; + tlsStream.flush (); + tlsStream >> test; + ASSERT_EQ (test, 123456789); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test get method. + */ +TEST_F (TlsSocketStream, get) +{ + Tls::Stream tlsStream; + tlsStream.get (); + ASSERT_TRUE (tlsStream.fail ()); + ASSERT_EQ (join::lastError, Errc::ConnectionClosed); + tlsStream.clear (); + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.write ("test", 4); + tlsStream.flush (); + ASSERT_EQ (tlsStream.get (), 't'); + ASSERT_EQ (tlsStream.get (), 'e'); + ASSERT_EQ (tlsStream.get (), 's'); + ASSERT_EQ (tlsStream.get (), 't'); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test peek method. + */ +TEST_F (TlsSocketStream, peek) +{ + Tls::Stream tlsStream; + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.write ("test", 4); + tlsStream.flush (); + ASSERT_EQ (tlsStream.peek (), 't'); + ASSERT_EQ (tlsStream.get (), 't'); + ASSERT_EQ (tlsStream.peek (), 'e'); + ASSERT_EQ (tlsStream.get (), 'e'); + ASSERT_EQ (tlsStream.peek (), 's'); + ASSERT_EQ (tlsStream.get (), 's'); + ASSERT_EQ (tlsStream.peek (), 't'); + ASSERT_EQ (tlsStream.get (), 't'); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test getline method. + */ +TEST_F (TlsSocketStream, getline) +{ + Tls::Stream tlsStream; + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.write ("test\n", 5); + tlsStream.flush (); + std::array test = {}; + tlsStream.getline (test.data (), test.size (), '\n'); + ASSERT_STREQ (test.data (), "test"); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test ignore method. + */ +TEST_F (TlsSocketStream, ignore) +{ + Tls::Stream tlsStream; + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.write ("test\n", 5); + tlsStream.flush (); + tlsStream.ignore (std::numeric_limits::max (), 'e'); + ASSERT_EQ (tlsStream.get (), 's'); + ASSERT_EQ (tlsStream.get (), 't'); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test read method. + */ +TEST_F (TlsSocketStream, read) +{ + Tls::Stream tlsStream; + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.write ("test", 4); + tlsStream.flush (); + std::array test = {}; + tlsStream.read (test.data (), 4); + ASSERT_STREQ (test.data (), "test"); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test readsome method. + */ +TEST_F (TlsSocketStream, DISABLED_readsome) +{ + Tls::Stream tlsStream; + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.write ("test", 4); + tlsStream.flush (); + std::array test = {}; + ASSERT_EQ (tlsStream.readsome (test.data (), test.size ()), 4); + ASSERT_STREQ (test.data (), "test"); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief Test gcount method. + */ +TEST_F (TlsSocketStream, gcount) +{ + Tls::Stream tlsStream; + tlsStream.connect ({_host, _port}); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.handshake (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.write ("test", 4); + tlsStream.flush (); + std::array test = {}; + tlsStream.read (test.data (), 4); + ASSERT_EQ (tlsStream.gcount (), 4); + tlsStream.disconnect (); + ASSERT_TRUE (tlsStream.good ()) << join::lastError.message (); + tlsStream.close (); +} + +/** + * @brief main function. + */ +int main (int argc, char** argv) +{ + join::initializeOpenSSL (); + testing::InitGoogleTest (&argc, argv); + return RUN_ALL_TESTS (); +} diff --git a/crypto/tests/tlswrapper_test.cpp b/crypto/tests/tls_wrapper_test.cpp similarity index 92% rename from crypto/tests/tlswrapper_test.cpp rename to crypto/tests/tls_wrapper_test.cpp index ae86b2e7..faada31a 100644 --- a/crypto/tests/tlswrapper_test.cpp +++ b/crypto/tests/tls_wrapper_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include #include @@ -38,8 +38,8 @@ using join::IpAddress; using join::ReactorThread; using join::EventHandler; using join::Tcp; +using join::Tls; using join::TlsContext; -using join::TlsWrapper; /** * @brief Class used to test the stream TLS wrapper API. @@ -216,7 +216,7 @@ class TlsSocket : public EventHandler, public ::testing::Test Tcp::Socket tcp = _acceptor.accept (); if (tcp.connected ()) { - TlsWrapper tls (std::move (tcp), _tlsContext); + Tls::Socket tls (std::move (tcp), _tlsContext); if (tls.waitHandshake (_timeout)) { char buf[1024]; @@ -292,7 +292,7 @@ const std::string TlsSocket::_invalidKey = "/tmp/tlssocket_test_invalid.key"; TEST_F (TlsSocket, move) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls1 (ctx, Tcp::Socket::Blocking), tls2 (ctx); + Tls::Socket tls1 (ctx, Tcp::Socket::Blocking), tls2 (ctx); ASSERT_EQ (tls1.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_TRUE (tls1.connected ()); @@ -312,15 +312,15 @@ TEST_F (TlsSocket, move) TEST_F (TlsSocket, open) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx); + Tls::Socket tls (ctx); - ASSERT_EQ (tls.open (Tcp::v4 ()), 0) << join::lastError.message (); - ASSERT_EQ (tls.open (Tcp::v4 ()), -1); + ASSERT_EQ (tls.open (Tls::v4 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls::v4 ()), -1); ASSERT_EQ (join::lastError, Errc::InUse); tls.close (); - ASSERT_EQ (tls.open (Tcp::v6 ()), 0) << join::lastError.message (); - ASSERT_EQ (tls.open (Tcp::v6 ()), -1); + ASSERT_EQ (tls.open (Tls::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls::v6 ()), -1); ASSERT_EQ (join::lastError, Errc::InUse); tls.close (); } @@ -331,7 +331,7 @@ TEST_F (TlsSocket, open) TEST_F (TlsSocket, close) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_FALSE (tls.opened ()); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); @@ -348,7 +348,7 @@ TEST_F (TlsSocket, close) TEST_F (TlsSocket, bind) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tls.bind (_hostv4), -1); @@ -367,7 +367,7 @@ TEST_F (TlsSocket, bind) TEST_F (TlsSocket, bindToDevice) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls.bindToDevice ("lo"), -1); @@ -376,7 +376,7 @@ TEST_F (TlsSocket, bindToDevice) ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); tls.close (); - ASSERT_EQ (tls.open (Tcp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls::v6 ()), 0) << join::lastError.message (); ASSERT_EQ (tls.bindToDevice ("lo"), 0) << join::lastError.message (); ASSERT_EQ (tls.connect ({_hostv6, _port}), 0) << join::lastError.message (); ASSERT_EQ (tls.disconnect (), 0) << join::lastError.message (); @@ -391,7 +391,7 @@ TEST_F (TlsSocket, bindToDevice) TEST_F (TlsSocket, connect) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls.connect ({"255.255.255.255", _port}), -1); @@ -410,7 +410,7 @@ TEST_F (TlsSocket, connect) TEST_F (TlsSocket, waitConnected) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx); + Tls::Socket tls (ctx); ASSERT_FALSE (tls.waitConnected (_timeout)); if (tls.connect ({_hostv4, _port}) == -1) @@ -433,11 +433,11 @@ TEST_F (TlsSocket, waitConnected) TEST_F (TlsSocket, handshake) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls.handshake (), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); - ASSERT_EQ (tls.open (Tcp::v4 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls::v4 ()), 0) << join::lastError.message (); ASSERT_EQ (tls.handshake (), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); @@ -455,9 +455,9 @@ TEST_F (TlsSocket, handshake) TEST_F (TlsSocket, waitHandshake) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); - ASSERT_EQ (tls.open (Tcp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls::v6 ()), 0) << join::lastError.message (); ASSERT_FALSE (tls.waitHandshake (_timeout)); if (tls.connect ({_hostv6, _port}) == -1) { @@ -489,7 +489,7 @@ TEST_F (TlsSocket, waitHandshake) TEST_F (TlsSocket, disconnect) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_FALSE (tls.connected ()); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); @@ -506,7 +506,7 @@ TEST_F (TlsSocket, disconnect) TEST_F (TlsSocket, waitDisconnected) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); ASSERT_TRUE (tls.waitDisconnected (_timeout)) << join::lastError.message (); if (tls.connect ({_hostv4, _port}) == -1) @@ -529,7 +529,7 @@ TEST_F (TlsSocket, waitDisconnected) TEST_F (TlsSocket, waitReadyRead) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_FALSE (tls.waitReadyRead (_timeout)); @@ -566,7 +566,7 @@ TEST_F (TlsSocket, waitReadyRead) TEST_F (TlsSocket, read) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_EQ (tls.read (data, sizeof (data)), -1); @@ -585,7 +585,7 @@ TEST_F (TlsSocket, read) TEST_F (TlsSocket, readExactly) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_EQ (tls.readExactly (data, sizeof (data)), -1); @@ -607,7 +607,7 @@ TEST_F (TlsSocket, readExactly) TEST_F (TlsSocket, waitReadyWrite) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); ASSERT_FALSE (tls.waitReadyWrite (_timeout)); ASSERT_EQ (join::lastError, Errc::OperationFailed); @@ -641,7 +641,7 @@ TEST_F (TlsSocket, waitReadyWrite) TEST_F (TlsSocket, write) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_EQ (tls.write (data, sizeof (data)), -1); @@ -659,7 +659,7 @@ TEST_F (TlsSocket, write) TEST_F (TlsSocket, writeExactly) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); char data[] = {0x00, 0x65, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x5B, 0x22, 0x6B, 0x6F, 0x22, 0x5D}; ASSERT_EQ (tls.writeExactly (data, sizeof (data)), -1); @@ -680,7 +680,7 @@ TEST_F (TlsSocket, writeExactly) TEST_F (TlsSocket, setMode) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); ASSERT_EQ (tls.open (), 0) << join::lastError.message (); @@ -704,7 +704,7 @@ TEST_F (TlsSocket, setMode) TEST_F (TlsSocket, setOption) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); ASSERT_EQ (tls.setOption (Tcp::Socket::RcvBuffer, 1500), -1); ASSERT_EQ (join::lastError, Errc::OperationFailed); @@ -731,7 +731,7 @@ TEST_F (TlsSocket, setOption) ASSERT_EQ (join::lastError, std::errc::no_protocol_option); tls.close (); - ASSERT_EQ (tls.open (Tcp::v6 ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls::v6 ()), 0) << join::lastError.message (); ASSERT_EQ (tls.setOption (Tcp::Socket::NoDelay, 1), 0) << join::lastError.message (); ASSERT_EQ (tls.setOption (Tcp::Socket::KeepAlive, 1), 0) << join::lastError.message (); ASSERT_EQ (tls.setOption (Tcp::Socket::KeepIdle, 1), 0) << join::lastError.message (); @@ -760,7 +760,7 @@ TEST_F (TlsSocket, setOption) TEST_F (TlsSocket, localEndpoint) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls.localEndpoint (), Tcp::Endpoint{}); ASSERT_EQ (tls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); @@ -775,7 +775,7 @@ TEST_F (TlsSocket, localEndpoint) TEST_F (TlsSocket, remoteEndpoint) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls.remoteEndpoint (), Tcp::Endpoint{}); ASSERT_EQ (tls.bind ({_hostv4, uint16_t (_port + 1)}), 0) << join::lastError.message (); @@ -790,10 +790,10 @@ TEST_F (TlsSocket, remoteEndpoint) TEST_F (TlsSocket, opened) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_FALSE (tls.opened ()); - ASSERT_EQ (tls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); ASSERT_TRUE (tls.opened ()); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); @@ -811,10 +811,10 @@ TEST_F (TlsSocket, opened) TEST_F (TlsSocket, connected) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_FALSE (tls.connected ()); - ASSERT_EQ (tls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); ASSERT_FALSE (tls.connected ()); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); @@ -832,10 +832,10 @@ TEST_F (TlsSocket, connected) TEST_F (TlsSocket, encrypted) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_FALSE (tls.encrypted ()); - ASSERT_EQ (tls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); ASSERT_FALSE (tls.encrypted ()); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_FALSE (tls.encrypted ()); @@ -855,7 +855,7 @@ TEST_F (TlsSocket, encrypted) TEST_F (TlsSocket, family) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls.family (), AF_INET); @@ -874,7 +874,7 @@ TEST_F (TlsSocket, family) TEST_F (TlsSocket, type) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); ASSERT_EQ (tls.type (), SOCK_STREAM); } @@ -885,7 +885,7 @@ TEST_F (TlsSocket, type) TEST_F (TlsSocket, protocol) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::NonBlocking); + Tls::Socket tls (ctx, Tcp::Socket::NonBlocking); ASSERT_EQ (tls.protocol (), IPPROTO_TCP); } @@ -896,10 +896,10 @@ TEST_F (TlsSocket, protocol) TEST_F (TlsSocket, handle) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls.handle (), -1); - ASSERT_EQ (tls.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tls.open (Tls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); ASSERT_GT (tls.handle (), -1); ASSERT_EQ (tls.connect ({_hostv4, _port}), 0) << join::lastError.message (); ASSERT_EQ (tls.handshake (), 0) << join::lastError.message (); @@ -917,7 +917,7 @@ TEST_F (TlsSocket, handle) TEST_F (TlsSocket, mtu) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls (ctx, Tcp::Socket::Blocking); + Tls::Socket tls (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls.mtu (), -1); ASSERT_EQ (tls.connect ({"127.0.0.1", _port}), 0) << join::lastError.message (); @@ -949,7 +949,7 @@ TEST_F (TlsSocket, verify) Tcp::Endpoint endpoint{_hostv4, _port}; ctx.setVerify (false); - TlsWrapper tls1 (ctx, Tcp::Socket::Blocking); + Tls::Socket tls1 (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls1.connect (endpoint), 0) << join::lastError.message (); ASSERT_EQ (tls1.handshake (), 0) << join::lastError.message (); ASSERT_EQ (tls1.shutdown (), 0) << join::lastError.message (); @@ -957,14 +957,14 @@ TEST_F (TlsSocket, verify) tls1.close (); ctx.setVerify (true, 0); - TlsWrapper tls2 (ctx, Tcp::Socket::Blocking); + Tls::Socket tls2 (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls2.connect (endpoint), 0) << join::lastError.message (); ASSERT_EQ (tls2.handshake (), -1); ASSERT_EQ (tls2.disconnect (), 0) << join::lastError.message (); tls2.close (); ctx.setVerify (true, 1); - TlsWrapper tls3 (ctx, Tcp::Socket::Blocking); + Tls::Socket tls3 (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls3.connect (endpoint), 0) << join::lastError.message (); ASSERT_EQ (tls3.handshake (), -1); ASSERT_EQ (tls3.disconnect (), 0) << join::lastError.message (); @@ -973,14 +973,14 @@ TEST_F (TlsSocket, verify) endpoint.hostname ("localhost"); ctx.setVerify (true, 0); - TlsWrapper tls4 (ctx, Tcp::Socket::Blocking); + Tls::Socket tls4 (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls4.connect (endpoint), 0) << join::lastError.message (); ASSERT_EQ (tls4.handshake (), -1); ASSERT_EQ (tls4.disconnect (), 0) << join::lastError.message (); tls4.close (); ctx.setVerify (true, 1); - TlsWrapper tls5 (ctx, Tcp::Socket::Blocking); + Tls::Socket tls5 (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls5.connect (endpoint), 0) << join::lastError.message (); ASSERT_EQ (tls5.handshake (), -1); ASSERT_EQ (tls5.disconnect (), 0) << join::lastError.message (); @@ -989,14 +989,14 @@ TEST_F (TlsSocket, verify) ASSERT_EQ (ctx.setCaFile (_rootcert), 0) << join::lastError.message (); ctx.setVerify (true, 0); - TlsWrapper tls6 (ctx, Tcp::Socket::Blocking); + Tls::Socket tls6 (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls6.connect (endpoint), 0) << join::lastError.message (); ASSERT_EQ (tls6.handshake (), -1); ASSERT_EQ (tls6.disconnect (), 0) << join::lastError.message (); tls6.close (); ctx.setVerify (true, 1); - TlsWrapper tls7 (ctx, Tcp::Socket::Blocking); + Tls::Socket tls7 (ctx, Tcp::Socket::Blocking); ASSERT_EQ (tls7.connect (endpoint), 0) << join::lastError.message (); ASSERT_EQ (tls7.handshake (), 0) << join::lastError.message (); ASSERT_EQ (tls7.shutdown (), 0) << join::lastError.message (); @@ -1010,10 +1010,10 @@ TEST_F (TlsSocket, verify) TEST_F (TlsSocket, isLower) { TlsContext ctx (TlsContext::Role::TlsClient); - TlsWrapper tls1 (ctx), tls2 (ctx); + Tls::Socket tls1 (ctx), tls2 (ctx); - ASSERT_EQ (tls1.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); - ASSERT_EQ (tls2.open (IpAddress (_hostv4).family ()), 0) << join::lastError.message (); + ASSERT_EQ (tls1.open (Tls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); + ASSERT_EQ (tls2.open (Tls (IpAddress (_hostv4).family ())), 0) << join::lastError.message (); if (tls1.handle () < tls2.handle ()) { ASSERT_TRUE (tls1 < tls2); diff --git a/data/tests/CMakeLists.txt b/data/tests/CMakeLists.txt index e59d5946..ad6e3e4a 100644 --- a/data/tests/CMakeLists.txt +++ b/data/tests/CMakeLists.txt @@ -7,55 +7,55 @@ target_link_libraries(dtoa.gtest ${JOIN_DATA} GTest::gtest_main) add_test(NAME base64.gtest COMMAND base64.gtest) install(TARGETS dtoa.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(stringview.gtest stringview_test.cpp) -target_link_libraries(stringview.gtest ${JOIN_DATA} GTest::gtest_main) -add_test(NAME stringview.gtest COMMAND stringview.gtest) -install(TARGETS stringview.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(string_view.gtest string_view_test.cpp) +target_link_libraries(string_view.gtest ${JOIN_DATA} GTest::gtest_main) +add_test(NAME string_view.gtest COMMAND string_view.gtest) +install(TARGETS string_view.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(streamview.gtest streamview_test.cpp) -target_link_libraries(streamview.gtest ${JOIN_DATA} GTest::gtest_main) -add_test(NAME streamview.gtest COMMAND streamview.gtest) -install(TARGETS streamview.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(stream_view.gtest stream_view_test.cpp) +target_link_libraries(stream_view.gtest ${JOIN_DATA} GTest::gtest_main) +add_test(NAME stream_view.gtest COMMAND stream_view.gtest) +install(TARGETS stream_view.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(value.gtest value_test.cpp) -target_link_libraries(value.gtest ${JOIN_DATA} GTest::gtest_main) +target_link_libraries(value.gtest ${JOIN_DATA} GTest::gtest_main) add_test(NAME value.gtest COMMAND value.gtest) install(TARGETS value.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(saxerror.gtest saxerror_test.cpp) -target_link_libraries(saxerror.gtest ${JOIN_DATA} GTest::gtest_main) -add_test(NAME saxerror.gtest COMMAND saxerror.gtest) -install(TARGETS saxerror.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(packreader.gtest packreader_test.cpp) -target_link_libraries(packreader.gtest ${JOIN_DATA} GTest::gtest_main) -add_test(NAME packreader.gtest COMMAND packreader.gtest) -install(TARGETS packreader.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(packwriter.gtest packwriter_test.cpp) -target_link_libraries(packwriter.gtest ${JOIN_DATA} GTest::gtest_main) -add_test(NAME packwriter.gtest COMMAND packwriter.gtest) -install(TARGETS packwriter.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(jsonerror.gtest jsonerror_test.cpp) -target_link_libraries(jsonerror.gtest ${JOIN_DATA} GTest::gtest_main) -add_test(NAME jsonerror.gtest COMMAND jsonerror.gtest) -install(TARGETS jsonerror.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(jsonreader.gtest jsonreader_test.cpp) -target_link_libraries(jsonreader.gtest ${JOIN_DATA} GTest::gtest_main) -add_test(NAME jsonreader.gtest COMMAND jsonreader.gtest) -install(TARGETS jsonreader.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(jsonwriter.gtest jsonwriter_test.cpp) -target_link_libraries(jsonwriter.gtest ${JOIN_DATA} GTest::gtest_main) -add_test(NAME jsonwriter.gtest COMMAND jsonwriter.gtest) -install(TARGETS jsonwriter.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(jsoncanonicalizer.gtest jsoncanonicalizer_test.cpp) -target_link_libraries(jsoncanonicalizer.gtest ${JOIN_DATA} GTest::gtest_main) -add_test(NAME jsoncanonicalizer.gtest COMMAND jsoncanonicalizer.gtest) -install(TARGETS jsoncanonicalizer.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(sax_error.gtest sax_error_test.cpp) +target_link_libraries(sax_error.gtest ${JOIN_DATA} GTest::gtest_main) +add_test(NAME sax_error.gtest COMMAND sax_error.gtest) +install(TARGETS sax_error.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(pack_reader.gtest pack_reader_test.cpp) +target_link_libraries(pack_reader.gtest ${JOIN_DATA} GTest::gtest_main) +add_test(NAME pack_reader.gtest COMMAND pack_reader.gtest) +install(TARGETS pack_reader.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(pack_writer.gtest pack_writer_test.cpp) +target_link_libraries(pack_writer.gtest ${JOIN_DATA} GTest::gtest_main) +add_test(NAME pack_writer.gtest COMMAND pack_writer.gtest) +install(TARGETS pack_writer.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(json_error.gtest json_error_test.cpp) +target_link_libraries(json_error.gtest ${JOIN_DATA} GTest::gtest_main) +add_test(NAME json_error.gtest COMMAND json_error.gtest) +install(TARGETS json_error.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(json_reader.gtest json_reader_test.cpp) +target_link_libraries(json_reader.gtest ${JOIN_DATA} GTest::gtest_main) +add_test(NAME json_reader.gtest COMMAND json_reader.gtest) +install(TARGETS json_reader.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(json_writer.gtest json_writer_test.cpp) +target_link_libraries(json_writer.gtest ${JOIN_DATA} GTest::gtest_main) +add_test(NAME json_writer.gtest COMMAND json_writer.gtest) +install(TARGETS json_writer.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(json_canonicalizer.gtest json_canonicalizer_test.cpp) +target_link_libraries(json_canonicalizer.gtest ${JOIN_DATA} GTest::gtest_main) +add_test(NAME json_canonicalizer.gtest COMMAND json_canonicalizer.gtest) +install(TARGETS json_canonicalizer.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) add_executable(roundtrip.gtest roundtrip_test.cpp) target_link_libraries(roundtrip.gtest ${JOIN_DATA} GTest::gtest_main) diff --git a/data/tests/jsoncanonicalizer_test.cpp b/data/tests/json_canonicalizer_test.cpp similarity index 100% rename from data/tests/jsoncanonicalizer_test.cpp rename to data/tests/json_canonicalizer_test.cpp diff --git a/data/tests/jsonerror_test.cpp b/data/tests/json_error_test.cpp similarity index 100% rename from data/tests/jsonerror_test.cpp rename to data/tests/json_error_test.cpp diff --git a/data/tests/jsonreader_test.cpp b/data/tests/json_reader_test.cpp similarity index 100% rename from data/tests/jsonreader_test.cpp rename to data/tests/json_reader_test.cpp diff --git a/data/tests/jsonwriter_test.cpp b/data/tests/json_writer_test.cpp similarity index 100% rename from data/tests/jsonwriter_test.cpp rename to data/tests/json_writer_test.cpp diff --git a/data/tests/packreader_test.cpp b/data/tests/pack_reader_test.cpp similarity index 100% rename from data/tests/packreader_test.cpp rename to data/tests/pack_reader_test.cpp diff --git a/data/tests/packwriter_test.cpp b/data/tests/pack_writer_test.cpp similarity index 100% rename from data/tests/packwriter_test.cpp rename to data/tests/pack_writer_test.cpp diff --git a/data/tests/saxerror_test.cpp b/data/tests/sax_error_test.cpp similarity index 100% rename from data/tests/saxerror_test.cpp rename to data/tests/sax_error_test.cpp diff --git a/data/tests/streamview_test.cpp b/data/tests/stream_view_test.cpp similarity index 100% rename from data/tests/streamview_test.cpp rename to data/tests/stream_view_test.cpp diff --git a/data/tests/stringview_test.cpp b/data/tests/string_view_test.cpp similarity index 100% rename from data/tests/stringview_test.cpp rename to data/tests/string_view_test.cpp diff --git a/fabric/CMakeLists.txt b/fabric/CMakeLists.txt index b566a8b1..4c8fc7fc 100644 --- a/fabric/CMakeLists.txt +++ b/fabric/CMakeLists.txt @@ -1,33 +1,33 @@ cmake_minimum_required(VERSION 3.22.1) set(PUBLIC_HEADERS - include/join/dns.hpp - include/join/dot.hpp - include/join/mdns.hpp + include/join/dns_protocol.hpp + include/join/mdns_protocol.hpp + include/join/dot_protocol.hpp include/join/dnsmessage.hpp include/join/resolver.hpp include/join/nameserver.hpp - include/join/netlink.hpp + include/join/netlink_protocol.hpp include/join/netlink_endpoint.hpp - include/join/netlinkmanager.hpp + include/join/netlink_manager.hpp include/join/neighbor.hpp - include/join/neighbormanager.hpp + include/join/neighbor_manager.hpp include/join/arp.hpp include/join/interface.hpp include/join/interfacemanager.hpp include/join/route.hpp - include/join/routemanager.hpp + include/join/route_manager.hpp ) set(SOURCES - src/netlinkmanager.cpp + src/netlink_manager.cpp src/neighbor.cpp - src/neighbormanager.cpp + src/neighbor_manager.cpp src/arp.cpp src/interface.cpp - src/interfacemanager.cpp + src/interface_manager.cpp src/route.cpp - src/routemanager.cpp + src/route_manager.cpp ) add_library(${JOIN_FABRIC} ${SOURCES}) diff --git a/fabric/include/join/arp.hpp b/fabric/include/join/arp.hpp index dc5b1d97..ff2e47bf 100644 --- a/fabric/include/join/arp.hpp +++ b/fabric/include/join/arp.hpp @@ -26,8 +26,8 @@ #define JOIN_FABRIC_ARP_HPP // libjoin. -#include -#include +#include +#include #include #include diff --git a/fabric/include/join/dnsmessage.hpp b/fabric/include/join/dns_message.hpp similarity index 99% rename from fabric/include/join/dnsmessage.hpp rename to fabric/include/join/dns_message.hpp index 3cb0cc7c..9bfe436c 100644 --- a/fabric/include/join/dnsmessage.hpp +++ b/fabric/include/join/dns_message.hpp @@ -22,11 +22,11 @@ * SOFTWARE. */ -#ifndef JOIN_FABRIC_DNSMESSAGE_HPP -#define JOIN_FABRIC_DNSMESSAGE_HPP +#ifndef JOIN_FABRIC_DNS_MESSAGE_HPP +#define JOIN_FABRIC_DNS_MESSAGE_HPP // libjoin. -#include +#include #include #include diff --git a/fabric/include/join/dns.hpp b/fabric/include/join/dns_protocol.hpp similarity index 95% rename from fabric/include/join/dns.hpp rename to fabric/include/join/dns_protocol.hpp index 02ede0c5..0cffb1b4 100644 --- a/fabric/include/join/dns.hpp +++ b/fabric/include/join/dns_protocol.hpp @@ -22,11 +22,11 @@ * SOFTWARE. */ -#ifndef JOIN_FABRIC_DNS_HPP -#define JOIN_FABRIC_DNS_HPP +#ifndef JOIN_FABRIC_DNS_PROTOCOL_HPP +#define JOIN_FABRIC_DNS_PROTOCOL_HPP // libjoin. -#include +#include namespace join { @@ -51,7 +51,7 @@ namespace join * @brief construct the DNS protocol instance. * @param family IP address family. */ - constexpr Dns (int family = AF_INET) noexcept + constexpr explicit Dns (int family = AF_INET) noexcept : _family (family) { } diff --git a/fabric/include/join/dot.hpp b/fabric/include/join/dot_protocol.hpp similarity index 92% rename from fabric/include/join/dot.hpp rename to fabric/include/join/dot_protocol.hpp index 33bc7ea8..6741ded4 100644 --- a/fabric/include/join/dot.hpp +++ b/fabric/include/join/dot_protocol.hpp @@ -22,11 +22,11 @@ * SOFTWARE. */ -#ifndef JOIN_FABRIC_DOT_HPP -#define JOIN_FABRIC_DOT_HPP +#ifndef JOIN_FABRIC_DOT_PROTOCOL_HPP +#define JOIN_FABRIC_DOT_PROTOCOL_HPP // libjoin. -#include +#include namespace join { @@ -39,15 +39,16 @@ namespace join class Dot { public: - using Endpoint = BasicInternetEndpoint; - using Socket = TlsWrapper>; + using Transport = Tcp; + using Endpoint = BasicInternetEndpoint; + using Socket = BasicTlsWrapper; using Resolver = BasicTlsResolver; /** * @brief construct the DoT protocol instance. * @param family IP address family. */ - constexpr Dot (int family = AF_INET) noexcept + constexpr explicit Dot (int family = AF_INET) noexcept : _family (family) { } diff --git a/fabric/include/join/interface.hpp b/fabric/include/join/interface.hpp index a54c8155..ab3f0d79 100644 --- a/fabric/include/join/interface.hpp +++ b/fabric/include/join/interface.hpp @@ -26,8 +26,8 @@ #define JOIN_FABRIC_INTERFACE_HPP // libjoin. -#include -#include +#include +#include #include // C++. diff --git a/fabric/include/join/interfacemanager.hpp b/fabric/include/join/interface_manager.hpp similarity index 99% rename from fabric/include/join/interfacemanager.hpp rename to fabric/include/join/interface_manager.hpp index 15bc15b6..8dcda6b5 100644 --- a/fabric/include/join/interfacemanager.hpp +++ b/fabric/include/join/interface_manager.hpp @@ -22,13 +22,13 @@ * SOFTWARE. */ -#ifndef JOIN_FABRIC_INTERFACEMANAGER_HPP -#define JOIN_FABRIC_INTERFACEMANAGER_HPP +#ifndef JOIN_FABRIC_INTERFACE_MANAGER_HPP +#define JOIN_FABRIC_INTERFACE_MANAGER_HPP // libjoin. -#include -#include -#include +#include +#include +#include #include // C++. diff --git a/fabric/include/join/mdns.hpp b/fabric/include/join/mdns_protocol.hpp similarity index 95% rename from fabric/include/join/mdns.hpp rename to fabric/include/join/mdns_protocol.hpp index 57505a96..ade08ba8 100644 --- a/fabric/include/join/mdns.hpp +++ b/fabric/include/join/mdns_protocol.hpp @@ -22,11 +22,11 @@ * SOFTWARE. */ -#ifndef JOIN_FABRIC_MDNS_HPP -#define JOIN_FABRIC_MDNS_HPP +#ifndef JOIN_FABRIC_MDNS_PROTOCOL_HPP +#define JOIN_FABRIC_MDNS_PROTOCOL_HPP // libjoin. -#include +#include namespace join { @@ -47,7 +47,7 @@ namespace join * @brief construct the mDNS protocol instance. * @param family IP address family. */ - constexpr Mdns (int family = AF_INET) noexcept + constexpr explicit Mdns (int family = AF_INET) noexcept : _family (family) { } diff --git a/fabric/include/join/nameserver.hpp b/fabric/include/join/nameserver.hpp index ac02cfae..181720c6 100644 --- a/fabric/include/join/nameserver.hpp +++ b/fabric/include/join/nameserver.hpp @@ -26,11 +26,12 @@ #define JOIN_FABRIC_NAMESERVER_HPP // libjoin. -#include +#include +#include +#include +#include #include #include -#include -#include namespace join { diff --git a/fabric/include/join/neighbor.hpp b/fabric/include/join/neighbor.hpp index 93725caa..4a5b10bb 100644 --- a/fabric/include/join/neighbor.hpp +++ b/fabric/include/join/neighbor.hpp @@ -26,8 +26,8 @@ #define JOIN_FABRIC_NEIGHBOR_HPP // libjoin. -#include -#include +#include +#include #include // C++. diff --git a/fabric/include/join/neighbormanager.hpp b/fabric/include/join/neighbor_manager.hpp similarity index 99% rename from fabric/include/join/neighbormanager.hpp rename to fabric/include/join/neighbor_manager.hpp index e2bd9951..f5f13af0 100644 --- a/fabric/include/join/neighbormanager.hpp +++ b/fabric/include/join/neighbor_manager.hpp @@ -22,11 +22,11 @@ * SOFTWARE. */ -#ifndef JOIN_FABRIC_NEIGHBORMANAGER_HPP -#define JOIN_FABRIC_NEIGHBORMANAGER_HPP +#ifndef JOIN_FABRIC_NEIGHBOR_MANAGER_HPP +#define JOIN_FABRIC_NEIGHBOR_MANAGER_HPP // libjoin. -#include +#include #include // C++. diff --git a/fabric/include/join/netlinkmanager.hpp b/fabric/include/join/netlink_manager.hpp similarity index 96% rename from fabric/include/join/netlinkmanager.hpp rename to fabric/include/join/netlink_manager.hpp index 374dd876..ae98d939 100644 --- a/fabric/include/join/netlinkmanager.hpp +++ b/fabric/include/join/netlink_manager.hpp @@ -22,12 +22,13 @@ * SOFTWARE. */ -#ifndef JOIN_FABRIC_NETLINKMANAGER_HPP -#define JOIN_FABRIC_NETLINKMANAGER_HPP +#ifndef JOIN_FABRIC_NETLINK_MANAGER_HPP +#define JOIN_FABRIC_NETLINK_MANAGER_HPP // libjoin. +#include +#include #include -#include #include #include @@ -46,7 +47,7 @@ namespace join /** * @brief base class for netlink-based managers. */ - class NetlinkManager : private Netlink::Socket, public EventHandler + class NetlinkManager : public EventHandler { public: /** @@ -197,6 +198,9 @@ namespace join return static_cast (0); } + /// underlying socket. + Netlink::Socket _socket; + /// internal buffer size. static constexpr size_t _bufferSize = 16384; diff --git a/fabric/include/join/netlink.hpp b/fabric/include/join/netlink_protocol.hpp similarity index 96% rename from fabric/include/join/netlink.hpp rename to fabric/include/join/netlink_protocol.hpp index 5d5c2a77..4588b010 100644 --- a/fabric/include/join/netlink.hpp +++ b/fabric/include/join/netlink_protocol.hpp @@ -22,12 +22,12 @@ * SOFTWARE. */ -#ifndef JOIN_FABRIC_NETLINK_HPP -#define JOIN_FABRIC_NETLINK_HPP +#ifndef JOIN_FABRIC_NETLINK_PROTOCOL_HPP +#define JOIN_FABRIC_NETLINK_PROTOCOL_HPP // libjoin. #include -#include +#include namespace join { diff --git a/fabric/include/join/resolver.hpp b/fabric/include/join/resolver.hpp index 1891e94d..b8483a66 100644 --- a/fabric/include/join/resolver.hpp +++ b/fabric/include/join/resolver.hpp @@ -26,11 +26,13 @@ #define JOIN_FABRIC_RESOLVER_HPP // libjoin. -#include +#include +#include +#include +#include +#include #include #include -#include -#include // C++. #include @@ -712,6 +714,11 @@ namespace join */ virtual int disconnect ([[maybe_unused]] std::chrono::milliseconds timeout = std::chrono::seconds (5)) noexcept { + if (this->_socket.handle () == -1) + { + return 0; + } + _reactor.delHandler (_socket.handle ()); if (_socket.disconnect () == -1) @@ -883,10 +890,11 @@ namespace join data.rdbuf ()->pubsetbuf (_buffer.get (), size); DnsPacket packet; - _message.deserialize (packet, data); - packet.src = _socket.localEndpoint ().ip (); - packet.dest = _socket.remoteEndpoint ().ip (); - packet.port = _socket.remoteEndpoint ().port (); + auto local = _socket.localEndpoint (); + auto remote = _socket.remoteEndpoint (); + packet.src = local.ip (); + packet.dest = remote.ip (); + packet.port = remote.port (); if (packet.flags & 0x8000) { @@ -1108,7 +1116,10 @@ namespace join /** * @brief destroy instance. */ - virtual ~BasicTlsResolver () noexcept = default; + virtual ~BasicTlsResolver () noexcept + { + disconnect (); + } /** * @brief resolve host name using system name servers and return all IP addresses found. @@ -1232,12 +1243,6 @@ namespace join } } - // if (!this->_socket.waitHandshake (timeout.count ())) - // { - // close (); - // return -1; - // } - this->_reactor.addHandler (this->_socket.handle (), this); return 0; @@ -1249,6 +1254,11 @@ namespace join */ int disconnect (std::chrono::milliseconds timeout = std::chrono::seconds (5)) noexcept override final { + if (this->_socket.handle () == -1) + { + return 0; + } + this->_reactor.delHandler (this->_socket.handle ()); if (this->_socket.shutdown () == -1) diff --git a/fabric/include/join/route.hpp b/fabric/include/join/route.hpp index 9e1456a1..7ecc61ce 100644 --- a/fabric/include/join/route.hpp +++ b/fabric/include/join/route.hpp @@ -26,7 +26,7 @@ #define JOIN_FABRIC_ROUTE_HPP // libjoin. -#include +#include #include // C++. diff --git a/fabric/include/join/routemanager.hpp b/fabric/include/join/route_manager.hpp similarity index 99% rename from fabric/include/join/routemanager.hpp rename to fabric/include/join/route_manager.hpp index 67ff514e..e628623a 100644 --- a/fabric/include/join/routemanager.hpp +++ b/fabric/include/join/route_manager.hpp @@ -22,11 +22,11 @@ * SOFTWARE. */ -#ifndef JOIN_FABRIC_ROUTEMANAGER_HPP -#define JOIN_FABRIC_ROUTEMANAGER_HPP +#ifndef JOIN_FABRIC_ROUTE_MANAGER_HPP +#define JOIN_FABRIC_ROUTE_MANAGER_HPP // libjoin. -#include +#include #include // C++. diff --git a/fabric/src/interface.cpp b/fabric/src/interface.cpp index ac8a294b..30706449 100644 --- a/fabric/src/interface.cpp +++ b/fabric/src/interface.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include #include diff --git a/fabric/src/interfacemanager.cpp b/fabric/src/interface_manager.cpp similarity index 99% rename from fabric/src/interfacemanager.cpp rename to fabric/src/interface_manager.cpp index 828d7092..9e087ce5 100644 --- a/fabric/src/interfacemanager.cpp +++ b/fabric/src/interface_manager.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // C. #include diff --git a/fabric/src/neighbor.cpp b/fabric/src/neighbor.cpp index 6566ce80..5fdac4f0 100644 --- a/fabric/src/neighbor.cpp +++ b/fabric/src/neighbor.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include using join::NeighborKey; using join::Neighbor; diff --git a/fabric/src/neighbormanager.cpp b/fabric/src/neighbor_manager.cpp similarity index 99% rename from fabric/src/neighbormanager.cpp rename to fabric/src/neighbor_manager.cpp index 83be6f1e..3ef71e59 100644 --- a/fabric/src/neighbormanager.cpp +++ b/fabric/src/neighbor_manager.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // C. #include diff --git a/fabric/src/netlinkmanager.cpp b/fabric/src/netlink_manager.cpp similarity index 94% rename from fabric/src/netlinkmanager.cpp rename to fabric/src/netlink_manager.cpp index dfc26aef..358b8bb3 100644 --- a/fabric/src/netlinkmanager.cpp +++ b/fabric/src/netlink_manager.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // C. #include @@ -42,8 +42,8 @@ NetlinkManager::NetlinkManager (uint32_t groups, Reactor& reactor) , _wakeup (eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC)) , _reactor (reactor) { - open (Netlink::rt ()); - bind (groups); + _socket.open (Netlink::rt ()); + _socket.bind (groups); } // ========================================================================= @@ -71,7 +71,7 @@ Reactor& NetlinkManager::reactor () const noexcept void NetlinkManager::start () { _reactor.addHandler (_wakeup, this); - _reactor.addHandler (handle (), this); + _reactor.addHandler (_socket.handle (), this); } // ========================================================================= @@ -80,7 +80,7 @@ void NetlinkManager::start () // ========================================================================= void NetlinkManager::stop () { - _reactor.delHandler (handle ()); + _reactor.delHandler (_socket.handle ()); _reactor.delHandler (_wakeup); } @@ -92,7 +92,7 @@ int NetlinkManager::sendRequest (struct nlmsghdr* nlh, bool sync, std::chrono::m { if (!sync) { - if (write (reinterpret_cast (nlh), nlh->nlmsg_len) == -1) + if (_socket.write (reinterpret_cast (nlh), nlh->nlmsg_len) == -1) { return -1; // LCOV_EXCL_LINE } @@ -111,7 +111,7 @@ int NetlinkManager::sendRequest (struct nlmsghdr* nlh, bool sync, std::chrono::m // LCOV_EXCL_STOP } - if (write (reinterpret_cast (nlh), nlh->nlmsg_len) == -1) + if (_socket.write (reinterpret_cast (nlh), nlh->nlmsg_len) == -1) { // LCOV_EXCL_START _pending.erase (inserted.first); @@ -159,7 +159,7 @@ void NetlinkManager::onReadable (int fd) return; } - ssize_t len = read (_buffer.get (), _bufferSize); + ssize_t len = _socket.read (_buffer.get (), _bufferSize); if (len != -1) { struct nlmsghdr* nlh = reinterpret_cast (_buffer.get ()); diff --git a/fabric/src/route.cpp b/fabric/src/route.cpp index 8a795384..aa426999 100644 --- a/fabric/src/route.cpp +++ b/fabric/src/route.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include using join::RouteKey; using join::Route; diff --git a/fabric/src/routemanager.cpp b/fabric/src/route_manager.cpp similarity index 99% rename from fabric/src/routemanager.cpp rename to fabric/src/route_manager.cpp index 19f0988f..8b39bc27 100644 --- a/fabric/src/routemanager.cpp +++ b/fabric/src/route_manager.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // C. #include diff --git a/fabric/tests/dns_message_test.cpp b/fabric/tests/dns_message_test.cpp index 04d48bc3..7b388630 100644 --- a/fabric/tests/dns_message_test.cpp +++ b/fabric/tests/dns_message_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/fabric/tests/dns_protocol_test.cpp b/fabric/tests/dns_protocol_test.cpp index b48cb3ab..b065e402 100644 --- a/fabric/tests/dns_protocol_test.cpp +++ b/fabric/tests/dns_protocol_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/fabric/tests/dot_protocol_test.cpp b/fabric/tests/dot_protocol_test.cpp index 893d11e4..8dd2579e 100644 --- a/fabric/tests/dot_protocol_test.cpp +++ b/fabric/tests/dot_protocol_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/fabric/tests/interface_manager_test.cpp b/fabric/tests/interface_manager_test.cpp index 4c62b904..58087ea0 100644 --- a/fabric/tests/interface_manager_test.cpp +++ b/fabric/tests/interface_manager_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include // Libraries. diff --git a/fabric/tests/interface_test.cpp b/fabric/tests/interface_test.cpp index f4eb1cf5..b7bca16c 100644 --- a/fabric/tests/interface_test.cpp +++ b/fabric/tests/interface_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include // Libraries. diff --git a/fabric/tests/mdns_protocol_test.cpp b/fabric/tests/mdns_protocol_test.cpp index e9eb7137..39d0d9cd 100644 --- a/fabric/tests/mdns_protocol_test.cpp +++ b/fabric/tests/mdns_protocol_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/fabric/tests/neighbor_manager_test.cpp b/fabric/tests/neighbor_manager_test.cpp index d50c249b..bb196e1f 100644 --- a/fabric/tests/neighbor_manager_test.cpp +++ b/fabric/tests/neighbor_manager_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/fabric/tests/neighbor_test.cpp b/fabric/tests/neighbor_test.cpp index bc115c49..44a251c3 100644 --- a/fabric/tests/neighbor_test.cpp +++ b/fabric/tests/neighbor_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/fabric/tests/netlink_endpoint_test.cpp b/fabric/tests/netlink_endpoint_test.cpp index f097b1d2..da856c3e 100644 --- a/fabric/tests/netlink_endpoint_test.cpp +++ b/fabric/tests/netlink_endpoint_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/fabric/tests/netlink_protocol_test.cpp b/fabric/tests/netlink_protocol_test.cpp index 22981513..fad46510 100644 --- a/fabric/tests/netlink_protocol_test.cpp +++ b/fabric/tests/netlink_protocol_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/fabric/tests/netlink_socket_test.cpp b/fabric/tests/netlink_socket_test.cpp index dbd20ff7..93a8f4dd 100644 --- a/fabric/tests/netlink_socket_test.cpp +++ b/fabric/tests/netlink_socket_test.cpp @@ -23,7 +23,9 @@ */ // libjoin. -#include +#include +#include +#include // Libraries. #include @@ -452,22 +454,6 @@ TEST_F (NetlinkSocket, connected) ASSERT_FALSE (netlinkSocket.connected ()); } -/** - * @brief Test encrypted method. - */ -TEST_F (NetlinkSocket, encrypted) -{ - Netlink::Socket netlinkSocket (Netlink::Socket::Blocking); - - ASSERT_FALSE (netlinkSocket.opened ()); - ASSERT_EQ (netlinkSocket.open (Netlink::rt ()), 0) << join::lastError.message (); - ASSERT_FALSE (netlinkSocket.encrypted ()); - ASSERT_EQ (netlinkSocket.connect (_groups), 0) << join::lastError.message (); - ASSERT_FALSE (netlinkSocket.encrypted ()); - netlinkSocket.close (); - ASSERT_FALSE (netlinkSocket.encrypted ()); -} - /** * @brief Test family method. */ diff --git a/fabric/tests/route_manager_test.cpp b/fabric/tests/route_manager_test.cpp index 2fed8df7..c3cb2a6b 100644 --- a/fabric/tests/route_manager_test.cpp +++ b/fabric/tests/route_manager_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include // Libraries. diff --git a/fabric/tests/route_test.cpp b/fabric/tests/route_test.cpp index 817cd823..69c1899a 100644 --- a/fabric/tests/route_test.cpp +++ b/fabric/tests/route_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include // Libraries. diff --git a/services/CMakeLists.txt b/services/CMakeLists.txt index 5626f970..e178091b 100644 --- a/services/CMakeLists.txt +++ b/services/CMakeLists.txt @@ -4,19 +4,19 @@ find_package(ZLIB REQUIRED) set(PUBLIC_HEADERS include/join/cache.hpp - include/join/chunkstream.hpp - include/join/httpmessage.hpp + include/join/chunk_stream.hpp + include/join/http_message.hpp include/join/httpclient.hpp include/join/httpserver.hpp - include/join/mailmessage.hpp + include/join/mail_message.hpp include/join/smtpclient.hpp ) set(SOURCES src/cache.cpp - src/chunkstream.cpp - src/httpmessage.cpp - src/mailmessage.cpp + src/chunk_stream.cpp + src/http_message.cpp + src/mail_message.cpp ) add_library(${JOIN_SERVICES} ${SOURCES}) @@ -58,6 +58,6 @@ if(JOIN_ENABLE_TESTS) add_subdirectory(tests) endif() -if(JOIN_ENABLE_SAMPLES) - add_subdirectory(samples) -endif() +# if(JOIN_ENABLE_SAMPLES) +# add_subdirectory(samples) +# endif() diff --git a/services/include/join/chunkstream.hpp b/services/include/join/chunk_stream.hpp similarity index 98% rename from services/include/join/chunkstream.hpp rename to services/include/join/chunk_stream.hpp index a7106c3b..2bfb49f3 100644 --- a/services/include/join/chunkstream.hpp +++ b/services/include/join/chunk_stream.hpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#ifndef JOIN_SERVICES_CHUNKTREAM_HPP -#define JOIN_SERVICES_CHUNKTREAM_HPP +#ifndef JOIN_SERVICES_CHUNK_TREAM_HPP +#define JOIN_SERVICES_CHUNK_TREAM_HPP // libjoin. #include diff --git a/services/include/join/httpmessage.hpp b/services/include/join/http_message.hpp similarity index 99% rename from services/include/join/httpmessage.hpp rename to services/include/join/http_message.hpp index 915165fe..74e2b7af 100644 --- a/services/include/join/httpmessage.hpp +++ b/services/include/join/http_message.hpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#ifndef JOIN_SERVICES_HTTPMESSAGE_HPP -#define JOIN_SERVICES_HTTPMESSAGE_HPP +#ifndef JOIN_SERVICES_HTTP_MESSAGE_HPP +#define JOIN_SERVICES_HTTP_MESSAGE_HPP // libjoin. #include diff --git a/services/include/join/httpclient.hpp b/services/include/join/httpclient.hpp index e3017aca..48408f3e 100644 --- a/services/include/join/httpclient.hpp +++ b/services/include/join/httpclient.hpp @@ -26,7 +26,7 @@ #define JOIN_SERVICES_HTTPCLIENT_HPP // libjoin. -#include +#include #include #include #include diff --git a/services/include/join/mailmessage.hpp b/services/include/join/mail_message.hpp similarity index 99% rename from services/include/join/mailmessage.hpp rename to services/include/join/mail_message.hpp index 9278a2fe..5dab9266 100644 --- a/services/include/join/mailmessage.hpp +++ b/services/include/join/mail_message.hpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#ifndef JOIN_SERVICES_MAILMESSAGE_HPP -#define JOIN_SERVICES_MAILMESSAGE_HPP +#ifndef JOIN_SERVICES_MAIL_MESSAGE_HPP +#define JOIN_SERVICES_MAIL_MESSAGE_HPP // libjoin. #include diff --git a/services/include/join/smtpclient.hpp b/services/include/join/smtpclient.hpp index e0cba28c..a8a63d21 100644 --- a/services/include/join/smtpclient.hpp +++ b/services/include/join/smtpclient.hpp @@ -26,7 +26,7 @@ #define JOIN_SERVICES_SMTPCLIENT_HPP // libjoin. -#include +#include #include #include #include diff --git a/services/src/cache.cpp b/services/src/cache.cpp index 32a4cda6..cf7d98f5 100644 --- a/services/src/cache.cpp +++ b/services/src/cache.cpp @@ -26,13 +26,13 @@ #include // C. -#include -#include #include #include #include #include #include +#include +#include using join::Cache; diff --git a/services/src/chunkstream.cpp b/services/src/chunk_stream.cpp similarity index 99% rename from services/src/chunkstream.cpp rename to services/src/chunk_stream.cpp index d0898b11..29ad17a6 100644 --- a/services/src/chunkstream.cpp +++ b/services/src/chunk_stream.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include // C++. diff --git a/services/src/httpmessage.cpp b/services/src/http_message.cpp similarity index 99% rename from services/src/httpmessage.cpp rename to services/src/http_message.cpp index 1e457aa1..ac636d35 100644 --- a/services/src/httpmessage.cpp +++ b/services/src/http_message.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include using join::Errc; using join::HttpErrc; diff --git a/services/src/mailmessage.cpp b/services/src/mail_message.cpp similarity index 99% rename from services/src/mailmessage.cpp rename to services/src/mail_message.cpp index 12022c25..a2fa8fec 100644 --- a/services/src/mailmessage.cpp +++ b/services/src/mail_message.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // C++. #include diff --git a/services/tests/CMakeLists.txt b/services/tests/CMakeLists.txt index 11274e29..4e9cfc79 100644 --- a/services/tests/CMakeLists.txt +++ b/services/tests/CMakeLists.txt @@ -7,57 +7,57 @@ target_link_libraries(cache.gtest ${JOIN_SERVICES} GTest::gtest_main) add_test(NAME cache.gtest COMMAND cache.gtest) install(TARGETS cache.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) -add_executable(chunkstream.gtest chunkstream_test.cpp) -target_link_libraries(chunkstream.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME chunkstream.gtest COMMAND chunkstream.gtest) -install(TARGETS chunkstream.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(httperror.gtest httperror_test.cpp) -target_link_libraries(httperror.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME httperror.gtest COMMAND httperror.gtest) -install(TARGETS httperror.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(httprequest.gtest httprequest_test.cpp) -target_link_libraries(httprequest.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME httprequest.gtest COMMAND httprequest.gtest) -install(TARGETS httprequest.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(httpresponse.gtest httpresponse_test.cpp) -target_link_libraries(httpresponse.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME httpresponse.gtest COMMAND httpresponse.gtest) -install(TARGETS httpresponse.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(http.gtest http_test.cpp) -target_link_libraries(http.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME http.gtest COMMAND http.gtest) -install(TARGETS http.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(https.gtest https_test.cpp) -target_link_libraries(https.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME https.gtest COMMAND https.gtest) -install(TARGETS https.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(mailsender.gtest mailsender_test.cpp) -target_link_libraries(mailsender.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME mailsender.gtest COMMAND mailsender.gtest) -install(TARGETS mailsender.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(mailrecipient.gtest mailrecipient_test.cpp) -target_link_libraries(mailrecipient.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME mailrecipient.gtest COMMAND mailrecipient.gtest) -install(TARGETS mailrecipient.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(mailmessage.gtest mailmessage_test.cpp) -target_link_libraries(mailmessage.gtest ${JOIN_SERVICES} GTest::gtest_main GTest::gmock_main) -add_test(NAME mailmessage.gtest COMMAND mailmessage.gtest) -install(TARGETS mailmessage.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(smtp.gtest smtp_test.cpp) -target_link_libraries(smtp.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME smtp.gtest COMMAND smtp.gtest) -install(TARGETS smtp.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) - -add_executable(smtps.gtest smtps_test.cpp) -target_link_libraries(smtps.gtest ${JOIN_SERVICES} GTest::gtest_main) -add_test(NAME smtps.gtest COMMAND smtps.gtest) -install(TARGETS smtps.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) +add_executable(chunk_stream.gtest chunk_stream_test.cpp) +target_link_libraries(chunk_stream.gtest ${JOIN_SERVICES} GTest::gtest_main) +add_test(NAME chunk_stream.gtest COMMAND chunk_stream.gtest) +install(TARGETS chunk_stream.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(http_error.gtest http_error_test.cpp) +target_link_libraries(http_error.gtest ${JOIN_SERVICES} GTest::gtest_main) +add_test(NAME http_error.gtest COMMAND http_error.gtest) +install(TARGETS http_error.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(http_request.gtest http_request_test.cpp) +target_link_libraries(http_request.gtest ${JOIN_SERVICES} GTest::gtest_main) +add_test(NAME http_request.gtest COMMAND http_request.gtest) +install(TARGETS http_request.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(http_response.gtest http_response_test.cpp) +target_link_libraries(http_response.gtest ${JOIN_SERVICES} GTest::gtest_main) +add_test(NAME http_response.gtest COMMAND http_response.gtest) +install(TARGETS http_response.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +# add_executable(http.gtest http_test.cpp) +# target_link_libraries(http.gtest ${JOIN_SERVICES} GTest::gtest_main) +# add_test(NAME http.gtest COMMAND http.gtest) +# install(TARGETS http.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +# add_executable(https.gtest https_test.cpp) +# target_link_libraries(https.gtest ${JOIN_SERVICES} GTest::gtest_main) +# add_test(NAME https.gtest COMMAND https.gtest) +# install(TARGETS https.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(mail_sender.gtest mail_sender_test.cpp) +target_link_libraries(mail_sender.gtest ${JOIN_SERVICES} GTest::gtest_main) +add_test(NAME mail_sender.gtest COMMAND mail_sender.gtest) +install(TARGETS mail_sender.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(mail_recipient.gtest mail_recipient_test.cpp) +target_link_libraries(mail_recipient.gtest ${JOIN_SERVICES} GTest::gtest_main) +add_test(NAME mail_recipient.gtest COMMAND mail_recipient.gtest) +install(TARGETS mail_recipient.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +add_executable(mail_message.gtest mail_message_test.cpp) +target_link_libraries(mail_message.gtest ${JOIN_SERVICES} GTest::gtest_main GTest::gmock_main) +add_test(NAME mail_message.gtest COMMAND mail_message.gtest) +install(TARGETS mail_message.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +# add_executable(smtp.gtest smtp_test.cpp) +# target_link_libraries(smtp.gtest ${JOIN_SERVICES} GTest::gtest_main) +# add_test(NAME smtp.gtest COMMAND smtp.gtest) +# install(TARGETS smtp.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) + +# add_executable(smtps.gtest smtps_test.cpp) +# target_link_libraries(smtps.gtest ${JOIN_SERVICES} GTest::gtest_main) +# add_test(NAME smtps.gtest COMMAND smtps.gtest) +# install(TARGETS smtps.gtest RUNTIME DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/test) diff --git a/services/tests/chunkstream_test.cpp b/services/tests/chunk_stream_test.cpp similarity index 99% rename from services/tests/chunkstream_test.cpp rename to services/tests/chunk_stream_test.cpp index 5d28c4bf..063cedee 100644 --- a/services/tests/chunkstream_test.cpp +++ b/services/tests/chunk_stream_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include #include // Libraries. diff --git a/services/tests/httperror_test.cpp b/services/tests/http_error_test.cpp similarity index 99% rename from services/tests/httperror_test.cpp rename to services/tests/http_error_test.cpp index ba7293ca..a1990fcc 100644 --- a/services/tests/httperror_test.cpp +++ b/services/tests/http_error_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/services/tests/httprequest_test.cpp b/services/tests/http_request_test.cpp similarity index 99% rename from services/tests/httprequest_test.cpp rename to services/tests/http_request_test.cpp index c6bb9833..a3b7647c 100644 --- a/services/tests/httprequest_test.cpp +++ b/services/tests/http_request_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/services/tests/httpresponse_test.cpp b/services/tests/http_response_test.cpp similarity index 99% rename from services/tests/httpresponse_test.cpp rename to services/tests/http_response_test.cpp index 3183073e..11798d91 100644 --- a/services/tests/httpresponse_test.cpp +++ b/services/tests/http_response_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/services/tests/mailmessage_test.cpp b/services/tests/mail_message_test.cpp similarity index 99% rename from services/tests/mailmessage_test.cpp rename to services/tests/mail_message_test.cpp index a83aa36c..a44fdb08 100644 --- a/services/tests/mailmessage_test.cpp +++ b/services/tests/mail_message_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/services/tests/mailrecipient_test.cpp b/services/tests/mail_recipient_test.cpp similarity index 99% rename from services/tests/mailrecipient_test.cpp rename to services/tests/mail_recipient_test.cpp index d3be6d6c..e39b1ace 100644 --- a/services/tests/mailrecipient_test.cpp +++ b/services/tests/mail_recipient_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include diff --git a/services/tests/mailsender_test.cpp b/services/tests/mail_sender_test.cpp similarity index 98% rename from services/tests/mailsender_test.cpp rename to services/tests/mail_sender_test.cpp index b995a3d8..d0b4ec5d 100644 --- a/services/tests/mailsender_test.cpp +++ b/services/tests/mail_sender_test.cpp @@ -23,7 +23,7 @@ */ // libjoin. -#include +#include // Libraries. #include From 0cf1b44d0f3f76463d8faae1c3acfbf23feb6711 Mon Sep 17 00:00:00 2001 From: mrabine Date: Fri, 12 Jun 2026 22:16:13 +0200 Subject: [PATCH 6/9] use socket obj --- fabric/include/join/nameserver.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric/include/join/nameserver.hpp b/fabric/include/join/nameserver.hpp index 181720c6..37823737 100644 --- a/fabric/include/join/nameserver.hpp +++ b/fabric/include/join/nameserver.hpp @@ -378,7 +378,7 @@ namespace join } #ifndef DEBUG - if (this->setOption (Socket::MulticastLoop, 0) == -1) + if (this->_socket.setOption (Socket::MulticastLoop, 0) == -1) { // LCOV_EXCL_START this->close (); From 387c56eda2ff1bc95926086047095732cafc764b Mon Sep 17 00:00:00 2001 From: mrabine Date: Mon, 22 Jun 2026 00:48:26 +0200 Subject: [PATCH 7/9] fix dns and dot tests --- crypto/include/join/tls_protocol.hpp | 36 -------------------------- crypto/tests/tls_protocol_test.cpp | 18 ------------- fabric/CMakeLists.txt | 4 +-- fabric/include/join/dot_protocol.hpp | 18 ------------- fabric/include/join/resolver.hpp | 1 + fabric/tests/dot_protocol_test.cpp | 20 -------------- services/include/join/chunk_stream.hpp | 4 +-- 7 files changed, 5 insertions(+), 96 deletions(-) diff --git a/crypto/include/join/tls_protocol.hpp b/crypto/include/join/tls_protocol.hpp index 6907bc3e..ad4dacd5 100644 --- a/crypto/include/join/tls_protocol.hpp +++ b/crypto/include/join/tls_protocol.hpp @@ -88,24 +88,6 @@ namespace join return _family; } - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_STREAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_TCP; - } - private: /// IP address family. int _family; @@ -181,24 +163,6 @@ namespace join return _family; } - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_DGRAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_UDP; - } - private: /// IP address family. int _family; diff --git a/crypto/tests/tls_protocol_test.cpp b/crypto/tests/tls_protocol_test.cpp index 6c63fd89..5bc707a0 100644 --- a/crypto/tests/tls_protocol_test.cpp +++ b/crypto/tests/tls_protocol_test.cpp @@ -44,24 +44,6 @@ TEST (TlsProtocol, family) ASSERT_EQ (Tls::v4 ().family (), AF_INET); } -/** - * @brief test the type method. - */ -TEST (TlsProtocol, type) -{ - ASSERT_EQ (Dtls ().type (), SOCK_DGRAM); - ASSERT_EQ (Tls ().type (), SOCK_STREAM); -} - -/** - * @brief test the protocol method. - */ -TEST (TlsProtocol, protocol) -{ - ASSERT_EQ (Dtls ().protocol (), IPPROTO_UDP); - ASSERT_EQ (Tls ().protocol (), IPPROTO_TCP); -} - /** * @brief equal method. */ diff --git a/fabric/CMakeLists.txt b/fabric/CMakeLists.txt index 4c8fc7fc..822fd153 100644 --- a/fabric/CMakeLists.txt +++ b/fabric/CMakeLists.txt @@ -4,7 +4,7 @@ set(PUBLIC_HEADERS include/join/dns_protocol.hpp include/join/mdns_protocol.hpp include/join/dot_protocol.hpp - include/join/dnsmessage.hpp + include/join/dns_message.hpp include/join/resolver.hpp include/join/nameserver.hpp include/join/netlink_protocol.hpp @@ -14,7 +14,7 @@ set(PUBLIC_HEADERS include/join/neighbor_manager.hpp include/join/arp.hpp include/join/interface.hpp - include/join/interfacemanager.hpp + include/join/interface_manager.hpp include/join/route.hpp include/join/route_manager.hpp ) diff --git a/fabric/include/join/dot_protocol.hpp b/fabric/include/join/dot_protocol.hpp index 6741ded4..6ae5d4df 100644 --- a/fabric/include/join/dot_protocol.hpp +++ b/fabric/include/join/dot_protocol.hpp @@ -82,24 +82,6 @@ namespace join return _family; } - /** - * @brief get the protocol communication semantic. - * @return the protocol communication semantic. - */ - constexpr int type () const noexcept - { - return SOCK_STREAM; - } - - /** - * @brief get the protocol type. - * @return the protocol type. - */ - constexpr int protocol () const noexcept - { - return IPPROTO_TCP; - } - /// default DoT port. static constexpr uint16_t defaultPort = 853; diff --git a/fabric/include/join/resolver.hpp b/fabric/include/join/resolver.hpp index b8483a66..7a4d8996 100644 --- a/fabric/include/join/resolver.hpp +++ b/fabric/include/join/resolver.hpp @@ -890,6 +890,7 @@ namespace join data.rdbuf ()->pubsetbuf (_buffer.get (), size); DnsPacket packet; + _message.deserialize (packet, data); auto local = _socket.localEndpoint (); auto remote = _socket.remoteEndpoint (); packet.src = local.ip (); diff --git a/fabric/tests/dot_protocol_test.cpp b/fabric/tests/dot_protocol_test.cpp index 8dd2579e..644e7df2 100644 --- a/fabric/tests/dot_protocol_test.cpp +++ b/fabric/tests/dot_protocol_test.cpp @@ -40,26 +40,6 @@ TEST (Dot, family) ASSERT_EQ (Dot::v6 ().family (), AF_INET6); } -/** - * @brief test the type method. - */ -TEST (Dot, type) -{ - ASSERT_EQ (Dot ().type (), SOCK_STREAM); - ASSERT_EQ (Dot::v4 ().type (), SOCK_STREAM); - ASSERT_EQ (Dot::v6 ().type (), SOCK_STREAM); -} - -/** - * @brief test the protocol method. - */ -TEST (Dot, protocol) -{ - ASSERT_EQ (Dot ().protocol (), IPPROTO_TCP); - ASSERT_EQ (Dot::v4 ().protocol (), IPPROTO_TCP); - ASSERT_EQ (Dot::v6 ().protocol (), IPPROTO_TCP); -} - /** * @brief test the equal method. */ diff --git a/services/include/join/chunk_stream.hpp b/services/include/join/chunk_stream.hpp index 2bfb49f3..d6291dba 100644 --- a/services/include/join/chunk_stream.hpp +++ b/services/include/join/chunk_stream.hpp @@ -22,8 +22,8 @@ * SOFTWARE. */ -#ifndef JOIN_SERVICES_CHUNK_TREAM_HPP -#define JOIN_SERVICES_CHUNK_TREAM_HPP +#ifndef JOIN_SERVICES_CHUNK_STREAM_HPP +#define JOIN_SERVICES_CHUNK_STREAM_HPP // libjoin. #include From 91e51c57ba289c31197ca15261005b9899865fe8 Mon Sep 17 00:00:00 2001 From: mrabine Date: Mon, 22 Jun 2026 02:23:05 +0200 Subject: [PATCH 8/9] typo --- crypto/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index c4a6fcbd..a8400f25 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -5,11 +5,10 @@ find_package(OpenSSL REQUIRED) set(PUBLIC_HEADERS include/join/base64.hpp include/join/openssl.hpp - include/join/openssl.hpp include/join/hmac.hpp include/join/tls_key.hpp include/join/signature.hpp - include/join/tl_serror.hpp + include/join/tls_error.hpp include/join/tls_context.hpp include/join/tls.hpp include/join/tls_wrapper.hpp From 7ffe350ca3a1a5a8a75be18df818ff0c0a430cb8 Mon Sep 17 00:00:00 2001 From: Mathieu Rabine Date: Mon, 22 Jun 2026 17:58:21 +0200 Subject: [PATCH 9/9] update codecov action --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 058434ba..ea55da40 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -223,7 +223,7 @@ jobs: fail-on-error: true - name: Deploy coverage report to codecov - uses: codecov/codecov-action@v5.5.3 + uses: codecov/codecov-action@v7.0.0 with: token: ${{secrets.CODECOV_TOKEN}} flags: unittests