# HG changeset patch # User David Demelier # Date 1480713737 -3600 # Node ID 05f830e0539b97aa7ef5322e0562bbe889a1b67b # Parent f322e5fcc099384fee37a278631b078907285d8b Net: use Boost.Asio instead diff -r f322e5fcc099 -r 05f830e0539b CMakeLists.txt --- a/CMakeLists.txt Fri Dec 02 22:21:52 2016 +0100 +++ b/CMakeLists.txt Fri Dec 02 22:22:17 2016 +0100 @@ -50,7 +50,6 @@ endif () add_subdirectory(modules/js) -add_subdirectory(modules/net) add_subdirectory(modules/unicode) add_subdirectory(modules/xdg) diff -r f322e5fcc099 -r 05f830e0539b modules/net/CMakeLists.txt --- a/modules/net/CMakeLists.txt Fri Dec 02 22:21:52 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -# -# CMakeLists.txt -- code building for common code -# -# Copyright (c) 2013-2016 David Demelier -# -# 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. -# - -find_package(OpenSSL) - -if (OPENSSL_FOUND) - code_define_module( - NAME net - SOURCES net.hpp - LIBRARIES $<$:ws2_32> ${OPENSSL_LIBRARIES} - INCLUDES ${OPENSSL_INCLUDE_DIR} - FLAGS DIRECTORY=\"${CMAKE_CURRENT_SOURCE_DIR}/test/\" - ) -endif () diff -r f322e5fcc099 -r 05f830e0539b modules/net/doc/mainpage.cpp --- a/modules/net/doc/mainpage.cpp Fri Dec 02 22:21:52 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * @mainpage - * - * Welcome to the net library. - * - * ## Introduction - * - * Portable and convenient library for networking in C++14. - * - * ## What it is? - * - * A clean and portable set of classes and function wrapping the BSD sockets family. It provides an API as close as - * possible to the original BSD sockets functions. - * - * It is implemented using templates to make sockets as type safe as possible. - * - * This module also adds the following: - * - * - Generic synchronous multiplexer based on select, poll, epoll and kqueue, - * - Convenient object-oriented option system, - * - Convenient object-oriented address system, - * - Experimental [OpenSSL][] support. - * - * ## What it is not? - * - * This module is not a high-level network library. It does not add asynchronous stuff like [Boost.Asio][asio] does. It is - * targeted to people who like BSD sockets and want low level facilities. It only depends on C++11 standard library and - * optionally [OpenSSL][]. - * - * ## Requirements - * - * - C++11. - * - OpenSSL, optional. - * - * ## Installation - * - * Just copy the file net.hpp and add it to your project. - * - * ## Overview - * - * ```` - * #include "net.h" - * - * try { - * net::SocketTcpIpv4 sc; - * - * sc.bind(net::address::Ipv4("*", 8080)); - * sc.listen(64); - * } catch (const std::exception &ex) { - * std::cerr << ex.what() << std::endl; - * std::exit(1); - * } - * ```` - * - * [asio]: http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio.html - * [OpenSSL]: http://openssl.org - */ diff -r f322e5fcc099 -r 05f830e0539b modules/net/net.hpp --- a/modules/net/net.hpp Fri Dec 02 22:21:52 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3746 +0,0 @@ -/* - * net.hpp -- portable C++ socket wrapper - * - * Copyright (c) 2013-2016 David Demelier - * - * 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 NET_HPP -#define NET_HPP - -/** - * \file net.hpp - * \brief Networking - * \author David Demelier - */ - -/** - * \defgroup net-module-tcp Network TCP support - * \brief TCP support in the networking module. - */ - -/** - * \defgroup net-module-udp Network UDP support - * \brief UDP support in networking module. - */ - -/** - * \defgroup net-module-tls Network TLS support - * \brief TLS support in networking module. - */ - -/** - * \defgroup net-module-addresses Network predefined addresses - * \brief Predefined addresses for sockets - */ - -/** - * \defgroup net-module-options Network predefined options - * \brief Predefined options for sockets - */ - -/** - * \defgroup net-module-backends Network predefined backends - * \brief Predefined backends for Listener - */ - -/** - * \defgroup net-module-resolv Network resolver - * \brief Resolv functions - */ - -/** - * \page Networking Networking - * - * - \subpage net-options - * - \subpage net-concepts - */ - -/** - * \page net-options User options - * - * The user may set the following variables before compiling these files: - * - * # General options - * - * - **NET_NO_AUTO_INIT**: (bool) Set to 0 if you don't want Socket class to - * automatically calls init function and finish functions. - * - * - **NET_NO_SSL**: (bool) Set to 0 if you don't have access to OpenSSL - * library. - * - * - **NET_NO_AUTO_SSL_INIT**: (bool) Set to 0 if you don't want Socket class - * with Tls to automatically init the OpenSSL library. You will need to call - * ssl::init and ssl::finish. - * - * # General compatibility options. - * - * The following options are auto detected but you can override them if you - * want. - * - * - **NET_HAVE_INET_PTON**: (bool) Set to 1 if you have inet_pton function. - * True for all platforms and Windows - * if _WIN32_WINNT is greater or equal to 0x0600. - * - * - **NET_HAVE_INET_NTOP**: (bool) Same as above. - * - * **Note:** On Windows, it is highly encouraged to set _WIN32_WINNT to at least - * 0x0600 on MinGW. - * - * # Options for Listener class - * - * Feature detection, multiple implementations may be avaible, for example, - * Linux has poll, select and epoll. - * - * We assume that `select(2)` is always available. - * - * Of course, you can set the variables yourself if you test it with your build - * system. - * - * - **NET_HAVE_POLL**: Defined on all BSD, Linux. Also defined on Windows - * if _WIN32_WINNT is set to 0x0600 or greater. - * - **NET_HAVE_KQUEUE**: Defined on all BSD and Apple. - * - **NET_HAVE_EPOLL**: Defined on Linux only. - * - **NET_DEFAULT_BACKEND**: Which backend to use (e.g. `Select`). - * - * The preference priority is ordered from left to right. - * - * | System | Backend | Class name | - * |---------------|-------------------------|--------------| - * | Linux | epoll(7) | Epoll | - * | *BSD | kqueue(2) | Kqueue | - * | Windows | poll(2), select(2) | Poll, Select | - * | Mac OS X | kqueue(2) | Kqueue | - */ - -/** - * \page net-concepts Concepts - * - * - \subpage net-concept-backend - * - \subpage net-concept-option - * - \subpage net-concept-stream - * - \subpage net-concept-datagram - */ - -/** - * \page net-concept-backend Backend (Concept) - * - * A backend is an interface for the Listener class. It is primarily designed to - * be the most suitable for the host environment. - * - * The backend must be default constructible, it is highly encouraged to be move - * constructible. - * - * This concepts requires the following functions: - * - * # name - * - * Get the backend name, informational only. - * - * ## Synopsis - * - * ```` - * std::string name() const; - * ```` - * - * ## Returns - * - * The backend name. - * - * # set - * - * Set one or more condition for the given handle. - * - * This erase previous flags if any. - * - * ## Synopsis - * - * ```` - * void set(const ListenerTable &table, Handle handle, Condition condition, - * bool add); - * ```` - * - * ## Arguments - * - * - **table**: the current table of sockets, - * - **handle**: the handle to set, - * - **condition**: the condition to add (may be OR'ed), - * - **add**: hint set to true if the handle is not currently registered. - * - * # unset - * - * Unset one or more condition for the given handle. - * - * ## Synopsis - * - * ```` - * void unset(const ListenerTable &table, Handle handle, Condition condition, - * bool remove); - * ```` - * - * ## Arguments - * - * - **table**: the current table of sockets, - * - **handle**: the handle to update, - * - **condition**: the condition to remove (may be OR'ed), - * - **remove**: hint set to true if the handle will be completely removed. - * - * # wait - * - * Wait for multiple sockets to be ready. - * - * ## Synopsis - * - * ```` - * std::vector wait(const ListenerTable &table, int ms); - * ```` - * - * ## Arguments - * - * - **table**: the current table, - * - **ms**: the number to wait in milliseconds, negative means forever. - * - * ## Returns - * - * The list of sockets ready paired to their condition. - */ - -/** - * \page net-concept-option Option (Concept) - * - * An option can be set or get from a socket. - * - * If an operation is not available, provides the function but throws an - * exception with ENOSYS message. - * - * This concepts requires the following functions: - * - * # Option (constructor) - * - * At least one default constructor must be present. - * - * ## Synopsis - * - * ```` - * Option() noexcept; - * ```` - * - * # set - * - * Set the option. - * - * ## Synopsis - * - * ```` - * template - * void set(Socket &sc) const; - * ```` - * - * ## Arguments - * - * - **sc**: the socket. - * - * # get - * - * Get an option, T can be any type. - * - * ## Synopsis - * - * ```` - * template - * T get(Socket &sc) const; - * ```` - * - * ## Arguments - * - * - **sc**: the socket. - * - * ## Returns - * - * The value. - */ - -/** - * \page net-concept-stream Stream (Concept) - * - * This concepts requires the following functions: - * - * # type - * - * Return the type of socket, usually `SOCK_STREAM`. - * - * ## Synopsis - * - * ```` - * int type() const noexcept; - * ```` - * - * ## Returns - * - * The type of socket. - * - * # connect - * - * Connect to the given address. - * - * ## Synopsis - * - * ```` - * void connect(const sockaddr *address, socklen_t length); // (0) - * void connect(const Address &address); // 1 (Optional) - * ```` - * - * ## Arguments - * - * - **address**: the address, - * - **length**: the address length. - * - * ## Throws - * - * - net::WouldBlockError: if the operation would block, - * - net::Error: on other error. - * - * # accept - * - * Accept a new client. - * - * If no pending connection is available and operation would block, the - * implementation must throw WouldBlockError. Any other error can be thrown - * otherwise a valid socket must be returned. - * - * ## Synopsis - * - * ```` - * Socket accept(); - * ```` - * - * ## Returns - * - * The new socket. - * - * ## Throws - * - * - net::WouldBlockError: if the operation would block, - * - net::Error: on other error. - * - * # recv - * - * Receive data. - * - * ## Synopsis - * - * ```` - * unsigned recv(void *data, unsigned length); - * ```` - * - * ## Arguments - * - * - **data**: the destination buffer, - * - **length**: the destination buffer length. - * - * ## Returns - * - * The number of bytes sent. - * - * ## Throws - * - * - net::WouldBlockError: if the operation would block, - * - net::Error: on other error. - * - * # send - * - * Send data. - * - * ## Synopsis - * - * ```` - * unsigned send(const void *data, unsigned length); - * ```` - * - * ## Arguments - * - * - **data**: the data to send, - * - **length**: the data length. - * - * ## Returns - * - * The number of bytes sent. - * - * ## Throws - * - * - net::WouldBlockError: if the operation would block, - * - net::Error: on other error. - */ - -/** - * \page net-concept-datagram Datagram (Concept) - * - * This concepts requires the following functions: - * - * # type - * - * Return the type of socket, usually `SOCK_DGRAM`. - * - * ## Synopsis - * - * ```` - * int type() const noexcept; - * ```` - * - * ## Returns - * - * The type of socket. - * - * # recvfrom - * - * Receive data. - * - * ## Synopsis - * - * ```` - * unsigned recvfrom(void *data, unsigned length, sockaddr *address, - * socklen_t *addrlen); - * unsigned recvfrom(void *data, unsigned length, Address *source) - * ```` - * - * ## Arguments - * - * - **data**: the data, - * - **length**: the length, - * - **address**: the source address, - * - **addrlen**: the source address in/out length. - * - * ## Returns - * - * The number of bytes received. - * - * ## Throws - * - * - net::WouldBlockError: if the operation would block, - * - net::Error: on other error. - * - * # sendto - * - * ```` - * unsigned sendto(const void *data, unsigned length, const sockaddr *address, - * socklen_t addrlen); - * unsigned sendto(const void *data, unsigned length, const Address &address); - * ```` - * - * ## Arguments - * - * - **data**: the data to send, - * - **length**: the data length, - * - **address**: the destination address, - * - **addrlen**: the destination address length. - * - * ## Returns - * - * The number of bytes sent. - * - * ## Throws - * - * - net::WouldBlockError: if the operation would block, - * - net::Error: on other error. - */ - -/* - * Headers to include. - * ------------------------------------------------------------------ - */ - -/* - * Include Windows headers before because it brings _WIN32_WINNT if not - * specified by the user. - */ -#if defined(_WIN32) -# include -# include -#else -# include -# include -# include -# include - -# include - -# include -# include - -# include -# include -# include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(NET_NO_SSL) - -#include -#include -#include - -#endif // !NET_NO_SSL - -/* - * Determine which I/O multiplexing implementations are available. - * ------------------------------------------------------------------ - * - * May define the following: - * - * - NET_HAVE_EPOLL - * - NET_HAVE_KQUEUE - * - NET_HAVE_POLL - */ - -#if defined(_WIN32) -# if _WIN32_WINNT >= 0x0600 && !defined(NET_HAVE_POLL) -# define NET_HAVE_POLL -# endif -#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) -# if !defined(NET_HAVE_KQUEUE) -# define NET_HAVE_KQUEUE -# endif -# if !defined(NET_HAVE_POLL) -# define NET_HAVE_POLL -# endif -#elif defined(__linux__) -# if !defined(NET_HAVE_EPOLL) -# define NET_HAVE_EPOLL -# endif -# if !defined(NET_HAVE_POLL) -# define NET_HAVE_POLL -# endif -#endif - -/* - * Compatibility macros. - * ------------------------------------------------------------------ - */ - -/** - * \brief Tells if inet_pton is available - */ -#if !defined(NET_HAVE_INET_PTON) -# if defined(_WIN32) -# if _WIN32_WINNT >= 0x0600 -# define NET_HAVE_INET_PTON -# endif -# else -# define NET_HAVE_INET_PTON -# endif -#endif - -/** - * \brief Tells if inet_ntop is available - */ -#if !defined(NET_HAVE_INET_NTOP) -# if defined(_WIN32) -# if _WIN32_WINNT >= 0x0600 -# define NET_HAVE_INET_NTOP -# endif -# else -# define NET_HAVE_INET_NTOP -# endif -#endif - -/* - * Define NET_DEFAULT_BACKEND. - * ------------------------------------------------------------------ - * - * Define the default I/O multiplexing implementation to use if not specified. - */ - -/** - * \brief Defines the default backend - */ -#if defined(_WIN32) -# if !defined(NET_DEFAULT_BACKEND) -# if defined(NET_HAVE_POLL) -# define NET_DEFAULT_BACKEND Poll -# else -# define NET_DEFAULT_BACKEND Select -# endif -# endif -#elif defined(__linux__) -# include - -# if !defined(NET_DEFAULT_BACKEND) -# define NET_DEFAULT_BACKEND Epoll -# endif -#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__) -# include -# include -# include - -# if !defined(NET_DEFAULT_BACKEND) -# define NET_DEFAULT_BACKEND Kqueue -# endif -#else -# if !defined(NET_DEFAULT_BACKEND) -# define NET_DEFAULT_BACKEND Select -# endif -#endif - -#if defined(NET_HAVE_POLL) && !defined(_WIN32) -# include -#endif - -/** - * The network namespace. - */ -namespace net { - -/* - * Portables types. - * ------------------------------------------------------------------ - * - * The following types are defined differently between Unix and Windows. - */ - -#if defined(_WIN32) - -/** - * Socket type, SOCKET. - */ -using Handle = SOCKET; - -/** - * Argument to pass to set. - */ -using ConstArg = const char *; - -/** - * Argument to pass to get. - */ -using Arg = char *; - -#else - -/** - * Socket type, int. - */ -using Handle = int; - -/** - * Argument to pass to set. - */ -using ConstArg = const void *; - -/** - * Argument to pass to get. - */ -using Arg = void *; - -#endif - -/* - * Portable constants. - * ------------------------------------------------------------------ - * - * These constants are needed to check functions return codes, they are rarely - * needed in end user code. - */ - -#if defined(_WIN32) - -/** - * Socket creation failure or invalidation. - */ -const Handle Invalid{INVALID_SOCKET}; - -/** - * Socket operation failure. - */ -const int Failure{SOCKET_ERROR}; - -#else - -/** - * Socket creation failure or invalidation. - */ -const Handle Invalid{-1}; - -/** - * Socket operation failure. - */ -const int Failure{-1}; - -#endif - -/** - * Close the socket library. - */ -inline void finish() noexcept -{ -#if defined(_WIN32) - WSACleanup(); -#endif -} - -/** - * Initialize the socket library. Except if you defined NET_NO_AUTO_INIT, you - * don't need to call this - * function manually. - */ -inline void init() noexcept -{ -#if defined(_WIN32) - static std::atomic initialized; - static std::mutex mutex; - - std::lock_guard lock(mutex); - - if (!initialized) { - initialized = true; - - WSADATA wsa; - WSAStartup(MAKEWORD(2, 2), &wsa); - - /* - * If NET_NO_AUTO_INIT is not set then the user must also call finish - * himself. - */ -#if !defined(NET_NO_AUTO_INIT) - atexit(finish); -#endif - } -#endif -} - -/** - * Get the last system error. - * - * \param errn the error number (errno or WSAGetLastError) - * \return the error - */ -inline std::string error(int errn) -{ -#if defined(_WIN32) - LPSTR str = nullptr; - std::string errmsg = "Unknown error"; - - FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - errn, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPSTR)&str, 0, NULL); - - - if (str) { - errmsg = std::string(str); - LocalFree(str); - } - - return errmsg; -#else - return strerror(errn); -#endif -} - -/** - * Get the last socket system error. The error is set from errno or from - * WSAGetLastError on Windows. - * - * \return a string message - */ -inline std::string error() -{ -#if defined(_WIN32) - return error(WSAGetLastError()); -#else - return error(errno); -#endif -} - -#if !defined(NET_NO_SSL) - -/** - * \brief SSL namespace - */ -namespace ssl { - -/** - * \enum Method - * \brief Which OpenSSL method to use. - */ -enum Method { - Tlsv1, //!< TLS v1.2 (recommended) - Sslv3 //!< SSLv3 -}; - -/** - * Initialize the OpenSSL library. Except if you defined NET_NO_AUTO_SSL_INIT, - * you don't need to call this function manually. - */ -inline void init() noexcept -{ - static std::atomic initialized; - static std::mutex mutex; - - std::lock_guard lock(mutex); - - if (!initialized) { - initialized = true; - - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - -#if !defined(NET_NO_AUTO_SSL_INIT) - atexit(finish); -#endif - } -} - -/** - * Close the OpenSSL library. - */ -inline void finish() noexcept -{ - ERR_free_strings(); -} - -} // !ssl - -#endif // !NET_NO_SSL - -/* - * Error class. - * ------------------------------------------------------------------ - * - * This is the main exception thrown on socket operations. - */ - -/** - * \brief Base class for sockets error. - */ -class Error : public std::exception { -private: - std::string m_message; - -public: - /** - * Construct the error using the specified error from the system. - * - * \param code the error code - * \warning the code must be a Winsock error or errno on Unix - */ - inline Error(int code) noexcept - : m_message(error(code)) - { - } - - /** - * Construct the error using the custom message. - * - * \param message the message - */ - inline Error(std::string message) noexcept - : m_message(std::move(message)) - { - } - - /** - * Construct the error using the last message from the system. - */ - inline Error() noexcept -#if defined(_WIN32) - : Error(WSAGetLastError()) -#else - : Error(errno) -#endif - { - } - - /** - * Get the error (only the error content). - * - * \return the error - */ - const char *what() const noexcept override - { - return m_message.c_str(); - } -}; - -/** - * \brief Timeout occured. - */ -class TimeoutError : public std::exception { -public: - /** - * Get the error message. - * - * \return the message - */ - const char *what() const noexcept override - { - return std::strerror(ETIMEDOUT); - } -}; - -/** - * \brief Operation would block. - */ -class WouldBlockError : public std::exception { -public: - /** - * Get the error message. - * - * \return the message - */ - const char *what() const noexcept override - { - return std::strerror(EWOULDBLOCK); - } -}; - -/** - * \brief Operation requires sending data to complete. - */ -class WantWriteError : public std::exception { -public: - /** - * Get the error message. - * - * \return the message - */ - const char *what() const noexcept override - { - return "operation requires writing to complete"; - } -}; - -/** - * \brief Operation requires reading data to complete. - */ -class WantReadError : public std::exception { -public: - /** - * Get the error message. - * - * \return the message - */ - const char *what() const noexcept override - { - return "operation requires read to complete"; - } -}; - -/* - * Condition enum - * ------------------------------------------------------------------ - * - * Defines if we must wait for reading or writing. - */ - -/** - * \enum Condition - * \brief Define the required condition for the socket. - */ -enum class Condition { - None, //!< No condition is required - Readable = (1 << 0), //!< The socket must be readable - Writable = (1 << 1), //!< The socket must be writable - All = 0x3 //! Both are requested -}; - -/** - * Apply bitwise XOR. - * - * \param v1 the first value - * \param v2 the second value - * \return the new value - */ -inline Condition operator^(Condition v1, Condition v2) noexcept -{ - return static_cast(static_cast(v1) ^ static_cast(v2)); -} - -/** - * Apply bitwise AND. - * - * \param v1 the first value - * \param v2 the second value - * \return the new value - */ -inline Condition operator&(Condition v1, Condition v2) noexcept -{ - return static_cast(static_cast(v1) & static_cast(v2)); -} - -/** - * Apply bitwise OR. - * - * \param v1 the first value - * \param v2 the second value - * \return the new value - */ -inline Condition operator|(Condition v1, Condition v2) noexcept -{ - return static_cast(static_cast(v1) | static_cast(v2)); -} - -/** - * Apply bitwise NOT. - * - * \param v the value - * \return the complement - */ -inline Condition operator~(Condition v) noexcept -{ - return static_cast(~static_cast(v)); -} - -/** - * Assign bitwise OR. - * - * \param v1 the first value - * \param v2 the second value - * \return the new value - */ -inline Condition &operator|=(Condition &v1, Condition v2) noexcept -{ - v1 = static_cast(static_cast(v1) | static_cast(v2)); - - return v1; -} - -/** - * Assign bitwise AND. - * - * \param v1 the first value - * \param v2 the second value - * \return the new value - */ -inline Condition &operator&=(Condition &v1, Condition v2) noexcept -{ - v1 = static_cast(static_cast(v1) & static_cast(v2)); - - return v1; -} - -/** - * Assign bitwise XOR. - * - * \param v1 the first value - * \param v2 the second value - * \return the new value - */ -inline Condition &operator^=(Condition &v1, Condition v2) noexcept -{ - v1 = static_cast(static_cast(v1) ^ static_cast(v2)); - - return v1; -} - -/** - * \brief Generic socket address storage. - * \ingroup net-module-addresses - */ -class Address { -private: - sockaddr_storage m_storage; - socklen_t m_length; - -public: - /** - * Construct empty address. - */ - inline Address() noexcept - : m_storage{} - , m_length(0) - { - } - - /** - * Construct address from existing one. - * - * \pre address != nullptr - * \param address the address - * \param length the address length - */ - inline Address(const sockaddr *address, socklen_t length) noexcept - : m_length(length) - { - assert(address); - - std::memcpy(&m_storage, address, length); - } - - /** - * Get the underlying address. - * - * \return the address - */ - inline const sockaddr *get() const noexcept - { - return reinterpret_cast(&m_storage); - } - - /** - * Overloaded function - * - * \return the address - */ - inline sockaddr *get() noexcept - { - return reinterpret_cast(&m_storage); - } - - /** - * Get the underlying address as the given type (e.g sockaddr_in). - * - * \return the address reference - */ - template - inline const T &as() const noexcept - { - return reinterpret_cast(m_storage); - } - - /** - * Overloaded function - * - * \return the address reference - */ - template - inline T &as() noexcept - { - return reinterpret_cast(m_storage); - } - - /** - * Get the underlying address length. - * - * \return the length - */ - inline socklen_t length() const noexcept - { - return m_length; - } - - /** - * Get the address domain. - * - * \return the domain - */ - inline int domain() const noexcept - { - return m_storage.ss_family; - } -}; - -/** - * \brief Address iterator. - * \ingroup net-module-addresses - * \see resolve - * - * This iterator can be used to try to connect to an host. - * - * When you use resolve with unspecified domain or socket type, the function may - * retrieve several different addresses that you can iterate over to try to - * connect to. - * - * Example: - * - * ````cpp - * SocketTcp sc; - * AddressIterator end, it = resolve("hostname.test", "80"); - * - * while (!connected_condition && it != end) - * sc.connect(it->address(), it->length()); - * ```` - * - * When an iterator equals to a default constructed iterator, it is considered - * not dereferenceable. - */ -class AddressIterator : public std::iterator { -private: - std::vector
m_addresses; - std::size_t m_index{0}; - -public: - /** - * Construct a null iterator. - * - * The default constructed iterator is not dereferenceable. - */ - inline AddressIterator() noexcept = default; - - /** - * Construct an address iterator with a set of addresses. - * - * \pre index < m_addresses.size() - * \param addresses the addresses - * \param index the first index - */ - inline AddressIterator(std::vector
addresses, std::size_t index = 0) noexcept - : m_addresses(std::move(addresses)) - , m_index(index) - { - assert(index < m_addresses.size()); - } - - /** - * Get the generic address. - * - * \pre this is dereferenceable - * \return the generic address - */ - inline const Address &operator*() const noexcept - { - assert(m_index <= m_addresses.size()); - - return m_addresses[m_index]; - } - - /** - * Overloaded function. - * - * \pre this is dereferenceable - * \return the generic address - */ - inline Address &operator*() noexcept - { - assert(m_index <= m_addresses.size()); - - return m_addresses[m_index]; - } - - /** - * Get the generic address. - * - * \pre this is dereferenceable - * \return the generic address - */ - inline const Address *operator->() const noexcept - { - assert(m_index <= m_addresses.size()); - - return &m_addresses[m_index]; - } - - /** - * Overloaded function. - * - * \pre this is dereferenceable - * \return the generic address - */ - inline Address *operator->() noexcept - { - assert(m_index <= m_addresses.size()); - - return &m_addresses[m_index]; - } - - /** - * Pre-increment the iterator. - * - * \return this - */ - inline AddressIterator &operator++(int) noexcept - { - if (m_index + 1 >= m_addresses.size()) { - m_addresses.clear(); - m_index = 0; - } else - m_index ++; - - return *this; - } - - /** - * Post-increment the iterator. - * - * \return copy of this - */ - inline AddressIterator operator++() noexcept - { - AddressIterator save = *this; - - if (m_index + 1 >= m_addresses.size()) { - m_addresses.clear(); - m_index = 0; - } else - m_index ++; - - return save; - } - - friend bool operator==(const AddressIterator &, const AddressIterator &) noexcept; - friend bool operator!=(const AddressIterator &, const AddressIterator &) noexcept; -}; - -/** - * Compare two address iterators. - * - * \param i1 the first iterator - * \param i2 the second iterator - * \return true if they equal - */ -inline bool operator==(const AddressIterator &i1, const AddressIterator &i2) noexcept -{ - return i1.m_addresses == i2.m_addresses && i1.m_index == i2.m_index; -} - -/** - * Compare two address iterators. - * - * \param i1 the first iterator - * \param i2 the second iterator - * \return false if they equal - */ -inline bool operator!=(const AddressIterator &i1, const AddressIterator &i2) noexcept -{ - return !(i1 == i2); -} - -/** - * Compare two generic addresses. - * - * \param a1 the first address - * \param a2 the second address - * \return true if they equal - */ -inline bool operator==(const Address &a1, const Address &a2) noexcept -{ - return a1.length() == a2.length() && std::memcmp(a1.get(), a2.get(), a1.length()) == 0; -} - -/** - * Compare two generic addresses. - * - * \param a1 the first address - * \param a2 the second address - * \return false if they equal - */ -inline bool operator!=(const Address &a1, const Address &a2) noexcept -{ - return !(a1 == a2); -} - -/** - * \brief Base socket class. - */ -class Socket { -protected: - /** - * The native handle. - */ - Handle m_handle{Invalid}; - -public: - /** - * Create a socket handle. - * - * This is the primary function and the only one that creates the socket - * handle, all other constructors are just overloaded functions. - * - * \param domain the domain AF_* - * \param type the type SOCK_* - * \param protocol the protocol - * \throw Error on errors - */ - Socket(int domain, int type, int protocol) - { -#if !defined(NET_NO_AUTO_INIT) - init(); -#endif - m_handle = ::socket(domain, type, protocol); - - if (m_handle == Invalid) - throw Error(); - } - - /** - * Create the socket with an already defined handle and its protocol. - * - * \param handle the handle - */ - explicit inline Socket(Handle handle) noexcept - : m_handle(handle) - { - } - - /** - * Create an invalid socket. Can be used when you cannot instanciate the - * socket immediately. - */ - explicit inline Socket(std::nullptr_t) noexcept - : m_handle(Invalid) - { - } - - /** - * Copy constructor deleted. - */ - Socket(const Socket &) = delete; - - /** - * Transfer ownership from other to this. - * - * \param other the other socket - */ - inline Socket(Socket &&other) noexcept - : m_handle(other.m_handle) - { - other.m_handle = Invalid; - } - - /** - * Default destructor. - */ - virtual ~Socket() - { - close(); - } - - /** - * Tells if the socket is not invalid. - * - * \return true if not invalid - */ - inline bool isOpen() const noexcept - { - return m_handle != Invalid; - } - - /** - * Set an option for the socket. Wrapper of setsockopt(2). - * - * \pre isOpen() - * \param level the setting level - * \param name the name - * \param arg the value - * \throw Error on errors - */ - template - inline void set(int level, int name, const Argument &arg) - { - assert(m_handle != Invalid); - - if (::setsockopt(m_handle, level, name, (ConstArg)&arg, sizeof (arg)) == Failure) - throw Error(); - } - - /** - * Object-oriented option setter. - * - * The object must have `set(Socket &) const`. - * - * \pre isOpen() - * \param option the option - * \throw Error on errors - */ - template - inline void set(const Option &option) - { - assert(m_handle != Invalid); - - option.set(*this); - } - - /** - * Get an option for the socket. Wrapper of getsockopt(2). - * - * \pre isOpen() - * \param level the setting level - * \param name the name - * \return the value - * \throw Error on errors - */ - template - Argument get(int level, int name) - { - assert(m_handle != Invalid); - - Argument desired, result{}; - socklen_t size = sizeof (result); - - if (::getsockopt(m_handle, level, name, (Arg)&desired, &size) == Failure) - throw Error(); - - std::memcpy(&result, &desired, size); - - return result; - } - - /** - * Object-oriented option getter. - * - * The object must have `T get(Socket &) const`, T can be any type and it is - * the value returned from this function. - * - * \pre isOpen() - * \return the same value as get() in the option - * \throw Error on errors - */ - template - inline auto get() -> decltype(std::declval