Mercurial > irccd
changeset 645:a63d73b456d5
Irccd: add transport_error
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 23 Mar 2018 21:15:10 +0100 |
parents | aae6d5a2b28d |
children | e4227aa185c2 |
files | libcommon/CMakeLists.txt libcommon/irccd/ini_util.hpp libirccd/irccd/daemon/server_util.cpp libirccd/irccd/daemon/transport_server.cpp libirccd/irccd/daemon/transport_server.hpp libirccd/irccd/daemon/transport_util.cpp libirccd/irccd/daemon/transport_util.hpp |
diffstat | 7 files changed, 305 insertions(+), 123 deletions(-) [+] |
line wrap: on
line diff
--- a/libcommon/CMakeLists.txt Fri Mar 23 14:00:03 2018 +0100 +++ b/libcommon/CMakeLists.txt Fri Mar 23 21:15:10 2018 +0100 @@ -25,6 +25,7 @@ ${libcommon_SOURCE_DIR}/irccd/config.hpp ${libcommon_SOURCE_DIR}/irccd/fs_util.hpp ${libcommon_SOURCE_DIR}/irccd/ini.hpp + ${libcommon_SOURCE_DIR}/irccd/ini_util.hpp ${libcommon_SOURCE_DIR}/irccd/json_util.hpp ${libcommon_SOURCE_DIR}/irccd/network_stream.hpp ${libcommon_SOURCE_DIR}/irccd/options.hpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libcommon/irccd/ini_util.hpp Fri Mar 23 21:15:10 2018 +0100 @@ -0,0 +1,97 @@ +/* + * ini_util.hpp -- ini utilities + * + * Copyright (c) 2013-2018 David Demelier <markand@malikania.fr> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IRCCD_COMMON_INI_UTIL_HPP +#define IRCCD_COMMON_INI_UTIL_HPP + +/** + * \file ini_util.hpp + * \brief Ini utilities. + */ + +#include <boost/optional.hpp> + +#include "ini.hpp" +#include "string_util.hpp" + +namespace irccd { + +/** + * \brief Ini utilities. + */ +namespace ini_util { + +/** + * Get an unsigned integer from the configuration section. + * + * \param sc the section + * \param name the option name + * \return the value or none if not able to convert + */ +template <typename Int> +inline boost::optional<Int> get_uint(const ini::section& sc, const std::string& name) noexcept +{ + return string_util::to_uint<Int>(sc.get(name).value()); +} + +/** + * Get an optional string or the default value if not given. + * + * \param sc the section + * \param name the option name + * \param def the default value + * \return the value or def if not found + */ +inline std::string optional_string(const ini::section& sc, + const std::string& name, + const std::string& def) noexcept +{ + const auto it = sc.find(name); + + if (it == sc.end()) + return def; + + return it->value(); +} + +/** + * Get an optional unsigned integer from the configuration section. + * + * \param sc the section + * \param name the option name + * \param def the default value + * \return the value or none if not able to convert + */ +template <typename Int> +inline boost::optional<Int> optional_uint(const ini::section& sc, + const std::string& name, + Int def) noexcept +{ + const auto it = sc.find(name); + + if (it == sc.end()) + return def; + + return string_util::to_uint<Int>(it->value()); +} + +} // !ini_util + +} // !irccd + +#endif // !IRCCD_COMMON_INI_UTIL_HPP
--- a/libirccd/irccd/daemon/server_util.cpp Fri Mar 23 14:00:03 2018 +0100 +++ b/libirccd/irccd/daemon/server_util.cpp Fri Mar 23 21:15:10 2018 +0100 @@ -19,7 +19,7 @@ #include <algorithm> #include <irccd/config.hpp> -#include <irccd/ini.hpp> +#include <irccd/ini_util.hpp> #include <irccd/json_util.hpp> #include <irccd/string_util.hpp> @@ -31,39 +31,12 @@ namespace { -// TODO: ini_util -std::string optional_string(const ini::section& sc, - const std::string& name, - const std::string& def) -{ - const auto it = sc.find(name); - - if (it == sc.end()) - return def; - - return it->value(); -} - -// TODO: ini_util -template <typename Int> -boost::optional<Int> optional_uint(const ini::section& sc, - const std::string& name, - Int def) -{ - const auto it = sc.find(name); - - if (it == sc.end()) - return def; - - return string_util::to_uint<Int>(it->value()); -} - void from_config_load_identity(server& sv, const ini::section& sc) { - const auto username = optional_string(sc, "username", sv.username()); - const auto realname = optional_string(sc, "realname", sv.realname()); - const auto nickname = optional_string(sc, "nickname", sv.nickname()); - const auto ctcp_version = optional_string(sc, "ctcp-version", sv.ctcp_version()); + const auto username = ini_util::optional_string(sc, "username", sv.username()); + const auto realname = ini_util::optional_string(sc, "realname", sv.realname()); + const auto nickname = ini_util::optional_string(sc, "nickname", sv.nickname()); + const auto ctcp_version = ini_util::optional_string(sc, "ctcp-version", sv.ctcp_version()); if (username.empty()) throw server_error(server_error::invalid_username); @@ -117,10 +90,10 @@ void from_config_load_numeric_parameters(server& sv, const ini::section& sc) { - const auto port = optional_uint<std::uint16_t>(sc, "port", sv.port()); - const auto ping_timeout = optional_uint<uint16_t>(sc, "ping-timeout", sv.ping_timeout()); - const auto reco_tries = optional_uint<uint8_t>(sc, "reconnect-tries", sv.reconnect_tries()); - const auto reco_timeout = optional_uint<uint16_t>(sc, "reconnect-delay", sv.reconnect_delay()); + const auto port = ini_util::optional_uint<std::uint16_t>(sc, "port", sv.port()); + const auto ping_timeout = ini_util::optional_uint<uint16_t>(sc, "ping-timeout", sv.ping_timeout()); + const auto reco_tries = ini_util::optional_uint<uint8_t>(sc, "reconnect-tries", sv.reconnect_tries()); + const auto reco_timeout = ini_util::optional_uint<uint16_t>(sc, "reconnect-delay", sv.reconnect_delay()); if (!port) throw server_error(server_error::invalid_port); @@ -139,8 +112,8 @@ void from_config_load_options(server& sv, const ini::section& sc) { - const auto password = optional_string(sc, "password", ""); - const auto command_char = optional_string(sc, "command-char", sv.command_char()); + const auto password = ini_util::optional_string(sc, "password", ""); + const auto command_char = ini_util::optional_string(sc, "command-char", sv.command_char()); sv.set_password(password); sv.set_command_char(command_char);
--- a/libirccd/irccd/daemon/transport_server.cpp Fri Mar 23 14:00:03 2018 +0100 +++ b/libirccd/irccd/daemon/transport_server.cpp Fri Mar 23 21:15:10 2018 +0100 @@ -19,6 +19,7 @@ #include <irccd/sysconfig.hpp> #include <cassert> +#include <system_error> #include <irccd/json_util.hpp> @@ -101,4 +102,57 @@ }); } +transport_error::transport_error(error code) noexcept + : system_error(make_error_code(code)) +{ +} + +const std::error_category& transport_category() noexcept +{ + static const class category : public std::error_category { + public: + const char* name() const noexcept override + { + return "transport"; + } + + std::string message(int e) const override + { + switch (static_cast<transport_error::error>(e)) { + case transport_error::auth_required: + return "authentication required"; + case transport_error::invalid_auth: + return "invalid authentication"; + case transport_error::invalid_port: + return "invalid port"; + case transport_error::invalid_address: + return "invalid address"; + case transport_error::invalid_hostname: + return "invalid hostname"; + case transport_error::invalid_path: + return "invalid socket path"; + case transport_error::invalid_family: + return "invalid family"; + case transport_error::invalid_certificate: + return "invalid certificate"; + case transport_error::invalid_private_key: + return "invalid private key"; + case transport_error::ssl_disabled: + return "ssl is not enabled"; + case transport_error::not_supported: + return "transport not supported"; + default: + return "no error"; + } + } + } category; + + return category; +}; + +std::error_code make_error_code(transport_error::error e) noexcept +{ + return {static_cast<int>(e), transport_category()}; +} + } // !irccd
--- a/libirccd/irccd/daemon/transport_server.hpp Fri Mar 23 14:00:03 2018 +0100 +++ b/libirccd/irccd/daemon/transport_server.hpp Fri Mar 23 21:15:10 2018 +0100 @@ -129,6 +129,68 @@ } }; +class transport_error : public std::system_error { +public: + enum error { + //!< Authentication is required. + auth_required, + + //!< Authentication was invalid. + invalid_auth, + + //!< Invalid TCP/IP port. + invalid_port, + + //!< Invalid TCP/IP address. + invalid_address, + + //!< The specified host was invalid. + invalid_hostname, + + //!< Invalid unix local path. + invalid_path, + + //!< Invalid IPv4/IPv6 family. + invalid_family, + + //!< Invalid certificate given. + invalid_certificate, + + //!< Invalid private key given. + invalid_private_key, + + //!< SSL was requested but is disabled. + ssl_disabled, + + //!< Kind of transport not supported on this platform. + not_supported + }; + + transport_error(error err) noexcept; +}; + +/** + * Get the transport error category singleton. + * + * \return the singleton + */ +const std::error_category& transport_category() noexcept; + +/** + * Create a boost::system::error_code from server_error::error enum. + * + * \param e the error code + */ +std::error_code make_error_code(transport_error::error e) noexcept; + } // !irccd +namespace std { + +template <> +struct is_error_code_enum<irccd::transport_error::error> : public std::true_type { +}; + +} // !std + #endif // !IRCCD_DAEMON_TRANSPORT_SERVER_HPP
--- a/libirccd/irccd/daemon/transport_util.cpp Fri Mar 23 14:00:03 2018 +0100 +++ b/libirccd/irccd/daemon/transport_util.cpp Fri Mar 23 21:15:10 2018 +0100 @@ -20,7 +20,7 @@ #include <cassert> -#include <irccd/ini.hpp> +#include <irccd/ini_util.hpp> #include <irccd/string_util.hpp> #include <irccd/daemon/ip_transport_server.hpp> @@ -35,39 +35,18 @@ #include "transport_util.hpp" +using namespace boost::asio; +using namespace boost::asio::ip; + namespace irccd { namespace transport_util { namespace { -std::unique_ptr<transport_server> load_transport_ip(boost::asio::io_service& service, - const ini::section& sc) +tcp from_config_load_ip_protocol(const ini::section& sc) { - assert(sc.key() == "transport"); - - std::unique_ptr<transport_server> transport; - ini::section::const_iterator it; - - // Port. - if ((it = sc.find("port")) == sc.cend()) - throw std::invalid_argument("missing 'port' parameter"); - - auto port = string_util::to_uint<std::uint16_t>(it->value()); - - if (!port) - throw std::invalid_argument("invalid port number"); - - // Address. - std::string address = "*"; - - if ((it = sc.find("address")) != sc.end()) - address = it->value(); - - // 0011 - // ^ define IPv4 - // ^ define IPv6 - auto mode = 1U; + bool ipv4 = true, ipv6 = false; /* * Documentation stated family but code checked for 'domain' option. @@ -76,78 +55,93 @@ * * See #637 */ + ini::section::const_iterator it; + if ((it = sc.find("domain")) != sc.end() || (it = sc.find("family")) != sc.end()) { - mode = 0U; + ipv4 = ipv6 = false; for (const auto& v : *it) { if (v == "ipv4") - mode |= (1U << 0); + ipv4 = true; if (v == "ipv6") - mode |= (1U << 1); + ipv6 = true; } } - if (mode == 0U) - throw std::invalid_argument("family must at least have ipv4 or ipv6"); + if (!ipv4 && !ipv6) + throw transport_error(transport_error::invalid_family); + + return ipv4 ? tcp::v4() : tcp::v6(); +} + +tcp::endpoint from_config_load_ip_endpoint(const ini::section& sc) +{ + const auto port = ini_util::get_uint<std::uint16_t>(sc, "port"); + const auto address = ini_util::optional_string(sc, "address", "*"); - auto protocol = (mode & 0x2U) - ? boost::asio::ip::tcp::v4() - : boost::asio::ip::tcp::v6(); + if (!port) + throw transport_error(transport_error::invalid_port); + if (address.empty()) + throw transport_error(transport_error::invalid_address); - // Optional SSL. - std::string pkey; - std::string cert; + const auto protocol = from_config_load_ip_protocol(sc); + + return address == "*" + ? tcp::endpoint(protocol, *port) + : tcp::endpoint(address::from_string(address), *port); +} - if ((it = sc.find("ssl")) != sc.end() && string_util::is_boolean(it->value())) { - if ((it = sc.find("certificate")) == sc.end()) - throw std::invalid_argument("missing 'certificate' parameter"); +tcp::acceptor from_config_load_ip_acceptor(io_service& service, const ini::section& sc) +{ + return tcp::acceptor(service, from_config_load_ip_endpoint(sc), true); +} + +std::unique_ptr<transport_server> from_config_load_ip(io_service& service, const ini::section& sc) +{ + assert(sc.key() == "transport"); + + auto acceptor = from_config_load_ip_acceptor(service, sc); - cert = it->value(); + if (string_util::is_boolean(sc.get("ssl").value())) { +#if !defined(HAVE_SSL) + throw transport_error(transport_error::ssl_disabled); +#else + const auto key = sc.get("key").value(); + const auto cert = sc.get("certificate").value(); - if ((it = sc.find("key")) == sc.end()) - throw std::invalid_argument("missing 'key' parameter"); + if (key.empty()) + throw transport_error(transport_error::invalid_private_key); + if (cert.empty()) + throw transport_error(transport_error::invalid_certificate); - pkey = it->value(); + boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); + + ctx.use_private_key_file(key, boost::asio::ssl::context::pem); + ctx.use_certificate_file(cert, boost::asio::ssl::context::pem); + + return std::make_unique<tls_transport_server>(std::move(acceptor), std::move(ctx)); +#endif } - auto endpoint = (address == "*") - ? boost::asio::ip::tcp::endpoint(protocol, *port) - : boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string(address), *port); - - boost::asio::ip::tcp::acceptor acceptor(service, endpoint, true); - - if (pkey.empty()) - return std::make_unique<ip_transport_server>(std::move(acceptor)); - -#if defined(HAVE_SSL) - boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23); - - ctx.use_private_key_file(pkey, boost::asio::ssl::context::pem); - ctx.use_certificate_file(cert, boost::asio::ssl::context::pem); - - return std::make_unique<tls_transport_server>(std::move(acceptor), std::move(ctx)); -#else - throw std::invalid_argument("SSL disabled"); -#endif + return std::make_unique<ip_transport_server>(std::move(acceptor)); } -std::unique_ptr<transport_server> load_transport_unix(boost::asio::io_service& service, - const ini::section& sc) +std::unique_ptr<transport_server> from_config_load_unix(io_service& service, const ini::section& sc) { assert(sc.key() == "transport"); #if !defined(IRCCD_SYSTEM_WINDOWS) using boost::asio::local::stream_protocol; - ini::section::const_iterator it = sc.find("path"); + const auto path = sc.get("path").value(); - if (it == sc.end()) - throw std::invalid_argument("missing 'path' parameter"); + if (path.empty()) + throw transport_error(transport_error::invalid_path); // Remove the file first. - std::remove(it->value().c_str()); + std::remove(path.c_str()); - stream_protocol::endpoint endpoint(it->value()); + stream_protocol::endpoint endpoint(path); stream_protocol::acceptor acceptor(service, std::move(endpoint)); return std::make_unique<local_transport_server>(std::move(acceptor)); @@ -155,32 +149,32 @@ (void)service; (void)sc; - throw std::invalid_argument("unix transports not supported on on this platform"); + throw transport_error(transport_error::not_supported); #endif } } // !namespace -std::unique_ptr<transport_server> from_config(boost::asio::io_service& service, const ini::section& sc) +std::unique_ptr<transport_server> from_config(io_service& service, const ini::section& sc) { assert(sc.key() == "transport"); - std::unique_ptr<transport_server> transport; - ini::section::const_iterator it = sc.find("type"); + const auto type = sc.get("type").value(); + const auto password = sc.get("password").value(); - if (it == sc.end()) - throw std::invalid_argument("missing 'type' parameter"); + if (type.empty()) + throw transport_error(transport_error::not_supported); + + std::unique_ptr<transport_server> transport; - if (it->value() == "ip") - transport = load_transport_ip(service, sc); - else if (it->value() == "unix") - transport = load_transport_unix(service, sc); + if (type == "ip") + transport = from_config_load_ip(service, sc); + else if (type == "unix") + transport = from_config_load_unix(service, sc); else - throw std::invalid_argument(string_util::sprintf("invalid type given: %s", it->value())); - + throw transport_error(transport_error::not_supported); - if ((it = sc.find("password")) != sc.end()) - transport->set_password(it->value()); + transport->set_password(password); return transport; }
--- a/libirccd/irccd/daemon/transport_util.hpp Fri Mar 23 14:00:03 2018 +0100 +++ b/libirccd/irccd/daemon/transport_util.hpp Fri Mar 23 21:15:10 2018 +0100 @@ -48,6 +48,7 @@ * * \param service the IO service * \param sc the configuration + * \throw transport_error on errors * \return the transport */ std::unique_ptr<transport_server> from_config(boost::asio::io_service& service,