diff options
| -rw-r--r-- | libcmix-network/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | libcmix-network/accept.cpp | 48 | ||||
| -rw-r--r-- | libcmix-network/accept.hpp | 38 | ||||
| -rw-r--r-- | libcmix-network/acceptor.cpp | 51 | ||||
| -rw-r--r-- | libcmix-network/acceptor.hpp | 47 | ||||
| -rw-r--r-- | libcmix-network/server.cpp | 31 | ||||
| -rw-r--r-- | libcmix-network/server.hpp | 38 | ||||
| -rw-r--r-- | node/node.cpp | 1 |
8 files changed, 193 insertions, 65 deletions
diff --git a/libcmix-network/CMakeLists.txt b/libcmix-network/CMakeLists.txt index 2574e82..1420d69 100644 --- a/libcmix-network/CMakeLists.txt +++ b/libcmix-network/CMakeLists.txt @@ -1,8 +1,11 @@ find_package(Boost COMPONENTS system REQUIRED) find_package(Threads) +find_package(OpenSSL REQUIRED) + add_library(cmix-network acceptor.hpp acceptor.cpp + accept.hpp accept.cpp connect.hpp connect.cpp server.hpp server.cpp client.hpp client.cpp @@ -21,6 +24,7 @@ target_include_directories(cmix-network target_link_libraries(cmix-network PRIVATE Boost::boost PUBLIC Boost::system + PUBLIC OpenSSL::SSL PUBLIC ${CMAKE_THREAD_LIBS_INIT} PRIVATE cmix PRIVATE log diff --git a/libcmix-network/accept.cpp b/libcmix-network/accept.cpp new file mode 100644 index 0000000..3fa4314 --- /dev/null +++ b/libcmix-network/accept.cpp @@ -0,0 +1,48 @@ + +#include "accept.hpp" + +#include <boost/bind.hpp> +#include <boost/asio/placeholders.hpp> + +using namespace boost::asio::ip; +using namespace boost::asio; + +void accept_connection(tcp::acceptor& acceptor, std::shared_ptr<tcp::socket> socket, boost::system::error_code ec, std::function<void (tcp::socket&&)> f) +{ + if(!bool(ec)) + { + f(std::move(*socket)); + accept_loop(acceptor, f); + } else { + std::stringstream ss; + ss << ec; + throw std::runtime_error(ss.str()); + } +} + +void accept_loop(tcp::acceptor& acceptor, std::function<void (tcp::socket&&)> f) +{ + std::shared_ptr<tcp::socket> new_socket = std::make_shared<tcp::socket>(acceptor.get_io_service()); + + acceptor.async_accept(*new_socket, boost::bind(accept_connection, boost::ref(acceptor), new_socket, placeholders::error, f)); +} + +void accept_connection(tcp::acceptor& acceptor, std::shared_ptr<ssl::context> ctx, std::shared_ptr<ssl::stream<tcp::socket>> socket, boost::system::error_code ec, std::function<void (ssl::stream<tcp::socket>&&)> f) +{ + if(!bool(ec)) + { + f(std::move(*socket)); + accept_loop(acceptor, ctx, f); + } else { + std::stringstream ss; + ss << ec; + throw std::runtime_error(ss.str()); + } +} + +void accept_loop(tcp::acceptor& acceptor, std::shared_ptr<ssl::context> ctx, std::function<void(ssl::stream<tcp::socket>&& socket)> f) { + + std::shared_ptr<ssl::stream<tcp::socket>> new_socket = std::make_shared<ssl::stream<tcp::socket>>(acceptor.get_io_service(), *ctx); + + acceptor.async_accept(new_socket->lowest_layer(), boost::bind(accept_connection, boost::ref(acceptor), ctx, new_socket, placeholders::error, f)); +} diff --git a/libcmix-network/accept.hpp b/libcmix-network/accept.hpp new file mode 100644 index 0000000..dde98a3 --- /dev/null +++ b/libcmix-network/accept.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include <boost/asio/ip/tcp.hpp> +#include <boost/asio/ssl.hpp> + +#include <memory> + +/*! + * \file + */ + +/*! + * \brief AcceptHandler Handy typedef for a function taking an tcp::socket. + */ +typedef std::function<void(boost::asio::ip::tcp::socket&&)> AcceptHandler; +/*! + * \brief SSLAcceptHandler Handy typedef for a function taking an "SSLSocket" + */ +typedef std::function<void(boost::asio::ssl::stream<boost::asio::ip::tcp::socket>&&)> SSLAcceptHandler; + +/*! + * \brief accept_loop Keeps accepting connections on the specified acceptor + * \param acceptor The acceptor should be in the listening state. + * \param f A callback function called with the accepted connection. + * + * The io_service of the acceptor is used as the io_service for the newly created socket. + */ +void accept_loop(boost::asio::ip::tcp::acceptor& acceptor, AcceptHandler f); + +/*! + * \brief accept_loop Keeps accepting SSL connections on the specified acceptor + * \param acceptor The acceptor should be in listening state. + * \param ctx The SSL context + * \param f A callback function called with the accepted connection. + * + * The io_service of the acceptor is used as the io_service for the newly created socket. + */ +void accept_loop(boost::asio::ip::tcp::acceptor& acceptor, std::shared_ptr<boost::asio::ssl::context> ctx, SSLAcceptHandler f);
\ No newline at end of file diff --git a/libcmix-network/acceptor.cpp b/libcmix-network/acceptor.cpp index 69b2807..ed640a1 100644 --- a/libcmix-network/acceptor.cpp +++ b/libcmix-network/acceptor.cpp @@ -1,42 +1,26 @@ #include "acceptor.hpp" -#include <boost/asio/ip/tcp.hpp> -#include <boost/asio/ip/v6_only.hpp> -#include <boost/asio/placeholders.hpp> -#include <boost/bind.hpp> +#include "accept.hpp" +#include <boost/asio/ip/v6_only.hpp> using namespace boost::asio; using namespace boost::asio::ip; -void accept_loop(tcp::acceptor& acceptor, std::function<void(tcp::socket&&)> f); - -void accept_connection(tcp::acceptor& acceptor, std::shared_ptr<tcp::socket> socket, boost::system::error_code ec, std::function<void(boost::asio::ip::tcp::socket&&)> f) -{ - if(!bool(ec)) - { - f(std::move(*socket)); - accept_loop(acceptor, f); - } else { - std::stringstream ss; - ss << ec; - throw std::runtime_error(ss.str()); - } -} - -void accept_loop(tcp::acceptor& acceptor, std::function<void(tcp::socket&&)> f) -{ - std::shared_ptr<tcp::socket> new_socket = std::make_shared<tcp::socket>(acceptor.get_io_service()); - - acceptor.async_accept(*new_socket, boost::bind(accept_connection, boost::ref(acceptor), new_socket, boost::asio::placeholders::error, f)); -} - Acceptor::Acceptor(boost::asio::io_service &io_service, boost::asio::ip::address address, uint16_t port) : acceptor(io_service) , endpoint(address, port) {} -void Acceptor::bind_v6_and_v4_any(std::function<void(tcp::socket&&)> accept_handler) { +address Acceptor::get_address() { + return endpoint.address(); +} + +bool Acceptor::is_open() { + return acceptor.is_open(); +} + +void Acceptor::listen_v6_and_v4_any() { acceptor.open(endpoint.protocol()); v6_only option(false); @@ -44,15 +28,22 @@ void Acceptor::bind_v6_and_v4_any(std::function<void(tcp::socket&&)> accept_hand acceptor.bind(endpoint); acceptor.listen(); - - accept_loop(acceptor, accept_handler); } -void Acceptor::setup_listen_socket(std::function<void (tcp::socket &&)> accept_handler) +void Acceptor::listen_socket() { acceptor.open(endpoint.protocol()); acceptor.bind(endpoint); acceptor.listen(); +} +void Acceptor::start_accepting(AcceptHandler accept_handler) { accept_loop(acceptor, accept_handler); } + +void Acceptor::start_accepting(std::shared_ptr<ssl::context> ctx, SSLAcceptHandler accept_handler) +{ + accept_loop(acceptor, ctx, accept_handler); +} + + diff --git a/libcmix-network/acceptor.hpp b/libcmix-network/acceptor.hpp index 06b709d..6e4de63 100644 --- a/libcmix-network/acceptor.hpp +++ b/libcmix-network/acceptor.hpp @@ -1,6 +1,9 @@ #pragma once +#include "accept.hpp" + #include <boost/asio/ip/tcp.hpp> +#include <boost/asio/ssl.hpp> #include <functional> @@ -15,8 +18,8 @@ * Otherwise they would be side to side in the Server class. */ class Acceptor{ - boost::asio::ip::tcp::acceptor acceptor; - boost::asio::ip::tcp::endpoint endpoint; + boost::asio::ip::tcp::acceptor acceptor; ///< The acceptor + boost::asio::ip::tcp::endpoint endpoint; ///< The endpoint public: /*! @@ -26,25 +29,49 @@ public: * \param port The listen port */ Acceptor(boost::asio::io_service& io_service, boost::asio::ip::address address, uint16_t port); - + /*! * \brief get_address * \return The listening address */ - boost::asio::ip::address get_address() { return endpoint.address(); } + boost::asio::ip::address get_address(); + + /*! + * \brief is_open Checks if the acceptor socket is opened. + * \return true if the socket is open, false otherwise. + */ + bool is_open(); /*! - * \brief bind_v6_and_v4_any - * \param accept_handler Function to call when accepting an incoming connection. + * \brief listen_v6_and_v4_any * * If you want to accept on both ipv6 and ipv any you have to do some special setup. * This function handles that and starts listening. */ - void bind_v6_and_v4_any(std::function<void(boost::asio::ip::tcp::socket&&)> accept_handler); + void listen_v6_and_v4_any(); + + /*! + * \brief listen_socket + * + * start listening on the specified endpoint + */ + void listen_socket(); + + /*! + * \brief start_accepting + * \param accept_handler The function called on a succesful accepted connection. + * + * Asynchronously indefinitely accepts connections. + */ + void start_accepting(AcceptHandler accept_handler); /*! - * \brief setup_listen_socket - * \param accept_handler Function to call when accepting an incoming connection. + * \brief start_accepting + * \param ctx The SSL context + * \param accept_handler The function called on a succesful accepted ssl connection. + * + * Asynchronously indefinitely accepts SSL connections. */ - void setup_listen_socket(std::function<void(boost::asio::ip::tcp::socket&&)> accept_handler); + void start_accepting(std::shared_ptr<boost::asio::ssl::context> ctx, SSLAcceptHandler accept_handler); + }; diff --git a/libcmix-network/server.cpp b/libcmix-network/server.cpp index cb2bd34..8dabecb 100644 --- a/libcmix-network/server.cpp +++ b/libcmix-network/server.cpp @@ -3,7 +3,7 @@ using namespace boost::asio::ip; using namespace boost::asio; -Server::Server(io_service& io_service, const ListenSettings& listen_settings, AcceptHandler accept_handler) +Server::Server(io_service& io_service, const ListenSettings& listen_settings) : listen_settings(listen_settings) , v4_acceptor(io_service, address_v4::from_string(listen_settings.ipv4_inaddr), listen_settings.port) , v6_acceptor(io_service, address_v6::from_string(listen_settings.ipv6_inaddr), listen_settings.port) @@ -16,16 +16,39 @@ Server::Server(io_service& io_service, const ListenSettings& listen_settings, Ac */ bool bind_v4_any = v4_acceptor.get_address().to_v4() == address_v4::any() && listen_settings.enable_ipv4; bool bind_v6_any = v6_acceptor.get_address().to_v6() == address_v6::any() && listen_settings.enable_ipv6; + if(bind_v4_any && bind_v6_any) { - v6_acceptor.bind_v6_and_v4_any(accept_handler); + v6_acceptor.listen_v6_and_v4_any(); } else if(bind_v4_any || bind_v6_any) { throw std::runtime_error("Cannot bind an INADDR_ANY and a non INADDR_ANY address on ipv4 and ipv6"); } else { if(listen_settings.enable_ipv4) { - v4_acceptor.setup_listen_socket(accept_handler); + v4_acceptor.listen_socket(); } if(listen_settings.enable_ipv6) { - v6_acceptor.setup_listen_socket(accept_handler); + v6_acceptor.listen_socket(); } } +} + +Server::Server(io_service& io_service, const ListenSettings& listen_settings, AcceptHandler accept_handler) +: Server(io_service, listen_settings) +{ + if(v4_acceptor.is_open()) { + v4_acceptor.start_accepting(accept_handler); + } + if(v6_acceptor.is_open()) { + v6_acceptor.start_accepting(accept_handler); + } +} + +Server::Server(io_service& io_service, const ListenSettings& listen_settings, std::shared_ptr<ssl::context> ctx, SSLAcceptHandler accept_handler) +: Server(io_service, listen_settings) +{ + if(v4_acceptor.is_open()) { + v4_acceptor.start_accepting(ctx, accept_handler); + } + if(v6_acceptor.is_open()) { + v6_acceptor.start_accepting(ctx, accept_handler); + } }
\ No newline at end of file diff --git a/libcmix-network/server.hpp b/libcmix-network/server.hpp index 2e5b272..fad7c71 100644 --- a/libcmix-network/server.hpp +++ b/libcmix-network/server.hpp @@ -1,6 +1,7 @@ #pragma once #include <boost/asio/io_service.hpp> +#include <boost/asio/ssl.hpp> #include "acceptor.hpp" @@ -13,10 +14,11 @@ */ struct ListenSettings { bool enable_ipv4; ///< Should we listen on ipv4 - std::string ipv4_inaddr; ///< listen on this ipv4 address + std::string ipv4_inaddr; ///< Listen on this ipv4 address bool enable_ipv6; ///< Should we listen on ipv6 - std::string ipv6_inaddr; ///< listen on this ipv6 address - uint16_t port; ///< listen on this port. + std::string ipv6_inaddr; ///< Listen on this ipv6 address + uint16_t port; ///< Listen on this port. + bool use_ssl; ///< Should we use ssl }; /*! @@ -24,27 +26,12 @@ struct ListenSettings { */ class Server { -public: - /*! - * \brief AcceptHandler - */ - typedef std::function<void(boost::asio::ip::tcp::socket&& socket)> AcceptHandler; + Server(boost::asio::io_service& io_service, ListenSettings const& listen_settings); -private: - /*! - * \brief listen_settings - */ - ListenSettings const& listen_settings; - - /*! - * \brief v4_acceptor - */ + ListenSettings const& listen_settings; Acceptor v4_acceptor; - /*! - * \brief v6_acceptor - */ Acceptor v6_acceptor; - + public: /*! * \brief Server @@ -54,4 +41,13 @@ public: */ Server(boost::asio::io_service& io_service, ListenSettings const& listen_settings, AcceptHandler accept_handler); + /*! + * \brief Server + * \param io_service The io_service this server will use for all its connections. + * \param listen_settings The parameters used for determining on what endpoints to listen. + * \param ctx The SSL context. + * \param accept_handler The function to call when SSL connections are established. + */ + Server(boost::asio::io_service& io_service, ListenSettings const& listen_settings, std::shared_ptr<boost::asio::ssl::context> ctx, SSLAcceptHandler accept_handler); + }; diff --git a/node/node.cpp b/node/node.cpp index 958990a..045eb13 100644 --- a/node/node.cpp +++ b/node/node.cpp @@ -70,4 +70,5 @@ void Node::start_initialisation() { } void Node::start_precomputation() { + io_service.stop(); } |
