Mercurial > code
changeset 513:ab70c638dc1d
Get rid of net (see https://bitbucket.org/markand/libnet)
author | David Demelier <markand@malikania.fr> |
---|---|
date | Tue, 05 Apr 2016 15:18:41 +0200 |
parents | 0002b8da93dc |
children | d89a5e9e5fa7 |
files | CMakeLists.txt cmake/Doxyfile.in modules/net/CMakeLists.txt modules/net/examples/blocking-accept.cpp modules/net/examples/blocking-connect.cpp modules/net/examples/elapsed-timer.cpp modules/net/examples/elapsed-timer.h modules/net/examples/non-blocking-accept.cpp modules/net/examples/non-blocking-connect.cpp modules/net/examples/test.crt modules/net/examples/test.key modules/net/net.cpp modules/net/net.h modules/net/test/main.cpp modules/net/test/test.crt modules/net/test/test.key |
diffstat | 16 files changed, 6 insertions(+), 4572 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Mon Apr 04 17:34:01 2016 +0200 +++ b/CMakeLists.txt Tue Apr 05 15:18:41 2016 +0200 @@ -32,9 +32,6 @@ enable_testing() -# Optional -find_package(OpenSSL) - # Doxygen target find_package(Doxygen) @@ -53,5 +50,4 @@ ) endif () -add_subdirectory(modules/net) add_subdirectory(modules/options)
--- a/cmake/Doxyfile.in Mon Apr 04 17:34:01 2016 +0200 +++ b/cmake/Doxyfile.in Tue Apr 05 15:18:41 2016 +0200 @@ -754,7 +754,7 @@ # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = @CMAKE_SOURCE_DIR@/modules +INPUT = @CMAKE_SOURCE_DIR@/modules/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -774,7 +774,7 @@ # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. -FILE_PATTERNS = +FILE_PATTERNS = *.h # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -789,7 +789,10 @@ # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = port \ + cmake \ + extern \ + net/examples # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded
--- a/modules/net/CMakeLists.txt Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -# -# CMakeLists.txt -- sockets module -# -# Copyright (c) 2013-2015 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. -# - -cmake_minimum_required(VERSION 3.0) -project(net) - -set( - SOURCES - ${net_SOURCE_DIR}/net.cpp - ${net_SOURCE_DIR}/net.h -) - -if (OPENSSL_FOUND) - if (WIN32) - set(LIBRARIES ws2_32) - endif () - - code_define_module( - NAME sockets - INCLUDES ${OPENSSL_INCLUDE_DIR} - LIBRARIES - ${LIBRARIES} - ${OPENSSL_LIBRARIES} - SOURCES - ${net_SOURCE_DIR}/net.cpp - ${net_SOURCE_DIR}/net.h - RESOURCES - ${net_SOURCE_DIR}/test/test.crt - ${net_SOURCE_DIR}/test/test.key - ) -endif ()
--- a/modules/net/examples/blocking-accept.cpp Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/* - * blocking-accept.cpp -- example of blocking accept - * - * Options: - * - WITH_PORT (int), the port to use (default: 16000) - * - WITH_TIMEOUT (int), number of seconds before giving up (default: 60) - * - WITH_SSL (bool), true to test with SSL (default: false) - */ - -#include <iostream> - -#include "sockets.h" - -#if !defined(WITH_PORT) -# define WITH_PORT 16000 -#endif - -#if !defined(WITH_TIMEOUT) -# define WITH_TIMEOUT 60 -#endif - -int main() -{ -#if defined(WITH_SSL) - net::SocketTls<net::address::Ip> master; - net::SocketTls<net::address::Ip> client{net::Invalid}; -#else - net::SocketTcp<net::address::Ip> master; - net::SocketTcp<net::address::Ip> client{net::Invalid}; -#endif - - net::Listener<> listener; - - try { - master.set(net::option::SockReuseAddress{true}); - master.bind(net::address::Ip{"*", WITH_PORT}); - master.listen(); - - listener.set(master.handle(), net::Condition::Readable); - listener.wait(std::chrono::seconds(WITH_TIMEOUT)); - - master.accept(client); - } catch (const net::Error &error) { - std::cerr << "error: " << error.what() << std::endl; - std::exit(1); - } - - std::cout << "Client successfully accepted!" << std::endl; - - return 0; -} -
--- a/modules/net/examples/blocking-connect.cpp Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/* - * blocking-connect.cpp -- example of blocking connect - * - * Options: - * - WITH_HOST (string literal), the host to try (default: "malikania.fr") - * - WITH_PORT (int), the port to use (default: 80) - * - WITH_TIMEOUT (int), number of seconds before giving up (default: 30) - * - WITH_SSL (bool), true to test with SSL (default: false) - */ - -#include <iostream> - -#if !defined(WITH_HOST) -# define WITH_HOST "malikania.fr" -#endif - -#if !defined(WITH_PORT) -# define WITH_PORT 80 -#endif - -#if !defined(WITH_TIMEOUT) -# define WITH_TIMEOUT 30 -#endif - -#include "elapsed-timer.h" -#include "sockets.h" - -int main() -{ -#if defined(WITH_SSL) - net::SocketTls<net::address::Ipv4> socket; -#else - net::SocketTcp<net::address::Ipv4> socket; -#endif - - try { - std::cout << "Trying to connect to " << WITH_HOST << ":" << WITH_PORT << std::endl; - socket.connect(net::address::Ip(WITH_HOST, WITH_PORT)); - } catch (const net::Error &error) { - std::cerr << "error: " << error.what() << std::endl; - std::exit(1); - } - - std::cout << "Successfully connected!" << std::endl; - - return 0; -} -
--- a/modules/net/examples/elapsed-timer.cpp Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - * elapsed-timer.cpp -- measure elapsed time - * - * Copyright (c) 2013-2015 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. - */ - -#include "elapsed-timer.h" - -using std::chrono::duration_cast; -using std::chrono::high_resolution_clock; -using std::chrono::milliseconds; - -ElapsedTimer::ElapsedTimer() noexcept -{ - m_last = high_resolution_clock::now(); -} - -void ElapsedTimer::pause() noexcept -{ - /* - * When we put the timer on pause, do not forget to set the already - * elapsed time. - */ - (void)elapsed(); - m_paused = true; -} - -void ElapsedTimer::restart() noexcept -{ - m_paused = false; - m_last = high_resolution_clock::now(); -} - -void ElapsedTimer::reset() noexcept -{ - m_elapsed = 0; - m_last = high_resolution_clock::now(); -} - -unsigned ElapsedTimer::elapsed() noexcept -{ - if (!m_paused) { - m_elapsed += duration_cast<milliseconds>(high_resolution_clock::now() - m_last).count(); - m_last = high_resolution_clock::now(); - } - - return m_elapsed; -} \ No newline at end of file
--- a/modules/net/examples/elapsed-timer.h Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/* - * elapsed-timer.h -- measure elapsed time - * - * Copyright (c) 2013-2015 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 _ELAPSED_TIMER_H_ -#define _ELAPSED_TIMER_H_ - -/** - * @file ElapsedTimer.h - * @brief Measure elapsed time - */ - -#include <chrono> - -/** - * @class ElapsedTimer - * @brief Measure elapsed time - * - * This class provides an abstraction to measure elapsed time since the - * construction of the object. - * - * It uses std::chrono::high_resolution_clock for more precision and uses - * milliseconds only. - */ -class ElapsedTimer { -public: - using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock>; - -private: - TimePoint m_last; - bool m_paused{false}; - unsigned m_elapsed{0}; - -public: - /** - * Construct the elapsed timer, start counting. - */ - ElapsedTimer() noexcept; - - /** - * Put the timer on pause, the already elapsed time is stored. - */ - void pause() noexcept; - - /** - * Restart the timer, does not reset it. - */ - void restart() noexcept; - - /** - * Reset the timer to 0. - */ - void reset() noexcept; - - /** - * Get the number of elapsed milliseconds. - * - * @return the milliseconds - */ - unsigned elapsed() noexcept; -}; - -#endif // !_ELAPSED_TIMER_H_
--- a/modules/net/examples/non-blocking-accept.cpp Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,89 +0,0 @@ -/* - * non-blocking-accept.cpp -- example of total non-blocking accept - * - * Options: - * - WITH_PORT (int), the port to use (default: 16000) - * - WITH_TIMEOUT (int), number of milliseconds before giving up (default: 3000) - * - WITH_SSL (bool), true to test with SSL (default: false) - */ - -#include <iostream> - -#include "elapsed-timer.h" -#include "sockets.h" - -#if !defined(WITH_PORT) -# define WITH_PORT 16000 -#endif - -#if !defined(WITH_TIMEOUT) -# define WITH_TIMEOUT 3000 -#endif - -int main() -{ -#if defined(WITH_SSL) - net::SocketTls<net::address::Ip> master; - net::SocketTls<net::address::Ip> client{net::Invalid}; -#else - net::SocketTcp<net::address::Ip> master; - net::SocketTcp<net::address::Ip> client{net::Invalid}; -#endif - - net::Listener<> listener; - net::Condition cond; - - ElapsedTimer timer; - - // 1. Create the master socket for listening. - try { - master.set(net::option::SockReuseAddress{true}); - master.set(net::option::SockBlockMode{false}); - master.bind(net::address::Ip{"*", WITH_PORT}); - master.listen(); - - listener.set(master.handle(), net::Condition::Readable); - } catch (const net::Error &error) { - std::cerr << "error: " << error.what() << std::endl; - std::exit(1); - } - - while (!client.isOpen() && timer.elapsed() < WITH_TIMEOUT) { - try { - if (!client.isOpen()) { - // 2. Wait for a pre-accept process. - listener.wait(std::chrono::seconds(WITH_TIMEOUT)); - master.accept(client, nullptr, &cond); - client.set(net::option::SockBlockMode(false)); - listener.remove(master.handle()); - - std::cout << "Accepting new client" << std::endl; - - if (cond != net::Condition::None) { - std::cout << "Client accept state not complete" << std::endl; - listener.set(client.handle(), cond); - } - } else { - // 3. Wait for the accept process to complete. - std::cout << "Continuing accept for the client" << std::endl; - - listener.wait(std::chrono::seconds(WITH_TIMEOUT - timer.elapsed())); - client.accept(&cond); - listener.remove(client.handle()); - - if (cond != net::Condition::None) { - std::cout << "Client accept state not complete" << std::endl; - listener.set(client.handle(), cond); - } - } - } catch (const net::Error &error) { - std::cerr << error.function() << ": " << error.what() << std::endl; - std::exit(1); - } - } - - if (client.isOpen()) - std::cout << "Client successfully accepted!" << std::endl; - - return 0; -}
--- a/modules/net/examples/non-blocking-connect.cpp Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -/* - * non-blocking-connect.cpp -- example of non blocking connect - * - * Options: - * - WITH_HOST (string literal), the host to try (default: "malikania.fr") - * - WITH_PORT (int), the port to use (default: 80) - * - WITH_TIMEOUT (int), number of milliseconds before giving up (default: 3000) - * - WITH_SSL (bool), true to test with SSL (default: false) - */ - -#include <iostream> - -#if !defined(WITH_HOST) -# define WITH_HOST "malikania.fr" -#endif - -#if !defined(WITH_PORT) -# define WITH_PORT 80 -#endif - -#if !defined(WITH_TIMEOUT) -# define WITH_TIMEOUT 3000 -#endif - -#include "elapsed-timer.h" -#include "sockets.h" - -int main() -{ -#if defined(WITH_SSL) - net::SocketTls<net::address::Ip> socket; -#else - net::SocketTcp<net::address::Ip> socket; -#endif - - net::Listener<> listener; - net::Condition cond; - ElapsedTimer timer; - - // 1. Set to non-blocking. - socket.set(net::option::SockBlockMode(false)); - - try { - std::cout << "Trying to connect to " << WITH_HOST << ":" << WITH_PORT << std::endl; - - // 2. Initial connection process. - socket.connect(net::address::Ip(WITH_HOST, WITH_PORT), &cond); - - while (cond != net::Condition::None && timer.elapsed() < WITH_TIMEOUT) { - listener.remove(socket.handle()); - - // 2. Now complete by waiting for the appropriate condition. - listener.set(socket.handle(), cond); - listener.wait(std::chrono::milliseconds(WITH_TIMEOUT - timer.elapsed())); - socket.connect(&cond); - } - } catch (const net::Error &error) { - std::cerr << "error: " << error.what() << std::endl; - std::exit(1); - } - - std::cout << "Successfully connected!" << std::endl; - - return 0; -}
--- a/modules/net/examples/test.crt Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICITCCAYoCCQCGm4grkVCohjANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJG -UjEPMA0GA1UECAwGRnJhbmNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 -eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEwMjYyMDM0NThaFw0yNTEw -MjMyMDM0NThaMFUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxITAfBgNV -BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0 -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDp13OqVyOWyv5QWD4xr+Duw6SZ -gU7D5huzsAOcneSI6JUhf+7Ecu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xO -L9fTTK+TwhP2hW/Rf/b2gWedhJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/m -Pdy219f6MAPgODJ/7QIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAJSnn/IBn1ZblfzP -rJO/lE1Jwpmx3B7+oR/e4fkZd6JR3s06umGYWr2H+TPl/5dj9x0gPokhoIL9zCGq -SxCPnOeaxjBkw7yh3Ks6m3xKxmK4aMpAtBHtwmbfQyIcgz71/lfCzbJ3WcKpn1ig -IZbByt5QSSPcFORRzJJa35eHBdfX ------END CERTIFICATE-----
--- a/modules/net/examples/test.key Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQDp13OqVyOWyv5QWD4xr+Duw6SZgU7D5huzsAOcneSI6JUhf+7E -cu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xOL9fTTK+TwhP2hW/Rf/b2gWed -hJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/mPdy219f6MAPgODJ/7QIDAQAB -AoGBANDt4ndQkgi56A1rOm50gVlzTg6lPPXFE/0xB5kYbcdxX0VmI7Q8KCMwTI9V -jD2rk3e3OPSjr6FpfhzyxylkXMBz2BL5NRNPowCJbiMgZOUIzlcWPKo0tgf1bZJx -YdB5U003ISGPPBjVOAjyizY7tJnaNvbpLQ0hbIAsvHPEAOnBAkEA9r3g8NQjPrvb -oIr5SMIxM8HDJ1/q+MEBSFtRFzQpmur6P64Jsu96zCyencUYTxs0L/sottrj6dPC -vjGCc6PjsQJBAPKdqK1knJv6Y95M2bnEwrymCFVdxCi7AxObStB+bg/+7mMCUqqX -j2g71bfvhYakHV7CiaYrrORChwj6vTbimv0CQGpd2IZ5LOhyW2+N+YDgFg3Vzac/ -ti+eJEto8kAqgHUELvUctZmpmypBYe9pc91GQO0ePKL3IaE/ZIhRF4d6c0ECQH9A -XiaD7PiKvjLs0A31u8ZCt4A+7BII9LYl73mntobBSbu4ji9Xyyn6qEAPa1ORZK49 -DwGPSuF2W2lESlYtSOkCQGrtczhx3IyJjk5e2Y1i/UddPKjviAysCSzcW6aVTNr9 -Y2L0sWmva2FKnkl9FDuEqxvmGr6OOkr5Ll7aWLzJri8= ------END RSA PRIVATE KEY-----
--- a/modules/net/net.cpp Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -/* - * net.cpp -- portable C++ socket wrappers - * - * Copyright (c) 2013-2015 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. - */ - -#include <cstdlib> -#include <atomic> -#include <mutex> - -#include "net.h" - -namespace net { - -/* - * Portable constants - * ------------------------------------------------------------------ - */ - -#if defined(_WIN32) - -const Handle Invalid{INVALID_SOCKET}; -const int Failure{SOCKET_ERROR}; - -#else - -const Handle Invalid{-1}; -const int Failure{-1}; - -#endif - -/* - * Portable functions - * ------------------------------------------------------------------ - */ - -#if defined(_WIN32) - -namespace { - -static std::mutex s_mutex; -static std::atomic<bool> s_initialized{false}; - -} // !namespace - -#endif // !_WIN32 - -void init() noexcept -{ -#if defined(_WIN32) - std::lock_guard<std::mutex> lock(s_mutex); - - if (!s_initialized) { - s_initialized = true; - - WSADATA wsa; - WSAStartup(MAKEWORD(2, 2), &wsa); - - /* - * If NET_WSA_NO_INIT is not set then the user - * must also call finish himself. - */ -#if !defined(NET_NO_AUTO_INIT) - atexit(finish); -#endif - } -#endif -} - -void finish() noexcept -{ -#if defined(_WIN32) - WSACleanup(); -#endif -} - -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 -} - -std::string error() -{ -#if defined(_WIN32) - return error(WSAGetLastError()); -#else - return error(errno); -#endif -} - -#if !defined(NET_NO_SSL) - -namespace ssl { - -namespace { - -std::mutex mutex; -std::atomic<bool> initialized{false}; - -} // !namespace - -void finish() noexcept -{ - ERR_free_strings(); -} - -void init() noexcept -{ - std::lock_guard<std::mutex> 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 - } -} - -} // !ssl - -#endif // !NET_NO_SSL - -Error::Error(Code code, std::string function) - : m_code(code) - , m_function(std::move(function)) - , m_error(error()) -{ -} - -Error::Error(Code code, std::string function, int n) - : m_code(code) - , m_function(std::move(function)) - , m_error(error(n)) -{ -} - -Error::Error(Code code, std::string function, std::string error) - : m_code(code) - , m_function(std::move(function)) - , m_error(std::move(error)) -{ -} - -} // !net \ No newline at end of file
--- a/modules/net/net.h Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3188 +0,0 @@ -/* - * net.h -- portable C++ socket wrappers - * - * Copyright (c) 2013-2015 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 NET_H -#define NET_H - -/** - * @brief Network configuration - * - * # Portable defines - * - * This file defines user options and portability stuff. - * - * ## 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 net::init function and net::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 net::ssl::init and net::ssl::finish. - * - * ### 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. - * - * ### 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 | - */ - -/* - * 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 functions - * ------------------------------------------------------------------ - */ - -#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 - -#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. - */ - -#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 <sys/epoll.h> - -# if !defined(NET_DEFAULT_BACKEND) -# define NET_DEFAULT_BACKEND Epoll -# endif -#elif defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__APPLE__) -# include <sys/types.h> -# include <sys/event.h> -# include <sys/time.h> - -# 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 <poll.h> -#endif - -/* - * Headers to include - * ------------------------------------------------------------------ - */ - -#if defined(_WIN32) -# include <cstdlib> - -# include <WinSock2.h> -# include <WS2tcpip.h> -#else -# include <cerrno> - -# include <sys/ioctl.h> -# include <sys/types.h> -# include <sys/socket.h> -# include <sys/un.h> - -# include <arpa/inet.h> - -# include <netinet/in.h> -# include <netinet/tcp.h> - -# include <fcntl.h> -# include <netdb.h> -# include <unistd.h> -#endif - -#include <algorithm> -#include <cassert> -#include <chrono> -#include <cstring> -#include <memory> -#include <unordered_map> -#include <vector> - -#if !defined(NET_NO_SSL) - -#include <openssl/err.h> -#include <openssl/evp.h> -#include <openssl/ssl.h> - -#endif // !NET_NO_SSL - -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. - */ -extern const Handle Invalid; - -/** - * Socket operation failure. - */ -extern const int Failure; - -#else - -/** - * Socket creation failure or invalidation. - */ -extern const int Invalid; - -/** - * Socket operation failure. - */ -extern const int Failure; - -#endif - -/* - * Portable functions - * ------------------------------------------------------------------ - * - * The following free functions can be used to initialize the library or to get the last system error. - */ - -/** - * Initialize the socket library. Except if you defined NET_NO_AUTO_INIT, you don't need to call this - * function manually. - */ -void init() noexcept; - -/** - * Close the socket library. - */ -void finish() noexcept; - -/** - * Get the last socket system error. The error is set from errno or from - * WSAGetLastError on Windows. - * - * @return a string message - */ -std::string error(); - -/** - * Get the last system error. - * - * @param errn the error number (errno or WSAGetLastError) - * @return the error - */ -std::string error(int errn); - -#if !defined(NET_NO_SSL) - -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. - */ -void init() noexcept; - -/** - * Close the OpenSSL library. - */ -void finish() noexcept; - -} // !ssl - -#endif // !NET_NO_SSL - -/* - * Error class - * ------------------------------------------------------------------ - * - * This is the main exception thrown on socket operations. - */ - -/** - * @class Error - * @brief Base class for sockets error - */ -class Error : public std::exception { -public: - /** - * @enum Code - * @brief Which kind of error - */ - enum Code { - Timeout, ///!< The action did timeout - System, ///!< There is a system error - Other ///!< Other custom error - }; - -private: - Code m_code; - std::string m_function; - std::string m_error; - -public: - /** - * Constructor that use the last system error. - * - * @param code which kind of error - * @param function the function name - */ - Error(Code code, std::string function); - - /** - * Constructor that use the system error set by the user. - * - * @param code which kind of error - * @param function the function name - * @param error the error - */ - Error(Code code, std::string function, int error); - - /** - * Constructor that set the error specified by the user. - * - * @param code which kind of error - * @param function the function name - * @param error the error - */ - Error(Code code, std::string function, std::string error); - - /** - * Get which function has triggered the error. - * - * @return the function name (e.g connect) - */ - inline const std::string &function() const noexcept - { - return m_function; - } - - /** - * The error code. - * - * @return the code - */ - inline Code code() const noexcept - { - return m_code; - } - - /** - * Get the error (only the error content). - * - * @return the error - */ - const char *what() const noexcept - { - return m_error.c_str(); - } -}; - -/* - * Condition enum - * ------------------------------------------------------------------ - * - * Defines if we must wait for reading or writing. - */ - -/** - * @enum Condition - * @brief Define the required condition for the socket. - * - * As explained in Action enumeration, some operations required to be called several times, before calling these - * operations, the user must wait the socket to be readable or writable. This can be checked with Socket::condition. - */ -enum class Condition { - None, //!< No condition is required - Readable = (1 << 0), //!< The socket must be readable - Writable = (1 << 1) //!< The socket must be writable -}; - -/** - * Apply bitwise XOR. - * - * @param v1 the first value - * @param v2 the second value - * @return the new value - */ -constexpr Condition operator^(Condition v1, Condition v2) noexcept -{ - return static_cast<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2)); -} - -/** - * Apply bitwise AND. - * - * @param v1 the first value - * @param v2 the second value - * @return the new value - */ -constexpr Condition operator&(Condition v1, Condition v2) noexcept -{ - return static_cast<Condition>(static_cast<int>(v1) & static_cast<int>(v2)); -} - -/** - * Apply bitwise OR. - * - * @param v1 the first value - * @param v2 the second value - * @return the new value - */ -constexpr Condition operator|(Condition v1, Condition v2) noexcept -{ - return static_cast<Condition>(static_cast<int>(v1) | static_cast<int>(v2)); -} - -/** - * Apply bitwise NOT. - * - * @param v the value - * @return the complement - */ -constexpr Condition operator~(Condition v) noexcept -{ - return static_cast<Condition>(~static_cast<int>(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<Condition>(static_cast<int>(v1) | static_cast<int>(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<Condition>(static_cast<int>(v1) & static_cast<int>(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<Condition>(static_cast<int>(v1) ^ static_cast<int>(v2)); - - return v1; -} - -/** - * @class ListenerStatus - * @brief Result of polling - * - * Result of a select call, returns the first ready socket found with its - * flags. - */ -class ListenerStatus { -public: - Handle socket; //!< which socket is ready - Condition flags; //!< the flags -}; - -/** - * Table used in the socket listener to store which sockets have been - * set in which directions. - */ -using ListenerTable = std::unordered_map<Handle, Condition>; - -/* - * Base Socket class - * ------------------------------------------------------------------ - * - * This base class has operations that are common to all types of sockets but you usually instanciate - * a SocketTcp or SocketUdp - */ - -/** - * @class Socket - * @brief Base socket class for socket operations. - * - * **Important:** When using non-blocking sockets, some considerations must be taken. See the implementation of the - * underlying protocol for more details. - * - * When using non-blocking sockets, it is important to pass the condition to functions which may block, they indicate - * the condition to wait to perform or continue the operation if they would block. - * - * For example, when trying to connect with non-blocking, user should do the following: - * - * 1. Call Socket::connect() with the condition, - * 2. Loop until condition is not set to Condition::None (or an exception is thrown), - * 3. Wait with a listener for the condition to be ready (see Listener::poll), - * 4. Call Socket::resumeConnect() with the condition again. - * - * @see protocol::Tls - * @see protocol::Tcp - * @see protocol::Udp - */ -template <typename Address, typename Protocol> -class Socket { -private: - Protocol m_proto; - -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 - * @param iface the implementation - * @throw net::Error on errors - */ - Socket(int domain, int type, int protocol, Protocol iface = {}) - : m_proto(std::move(iface)) - { -#if !defined(NET_NO_AUTO_INIT) - init(); -#endif - m_handle = ::socket(domain, type, protocol); - - if (m_handle == Invalid) - throw Error(Error::System, "socket"); - - m_proto.create(*this); - } - - /** - * This tries to create a socket. - * - * Domain and type are determined by the Address and Protocol object. - * - * @param protocol the protocol - * @param address which type of address - * @throw net::Error on errors - */ - explicit inline Socket(Protocol protocol = {}, const Address &address = {}) - : Socket(address.domain(), protocol.type(), 0, std::move(protocol)) - { - } - - explicit inline Socket(Handle handle, Protocol protocol = {}) noexcept - : m_proto(std::move(protocol)) - , 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_proto(std::move(other.m_proto)) - , m_handle(other.m_handle) - { - /* Invalidate other */ - other.m_handle = Invalid; - } - - /** - * Default destructor. - */ - virtual ~Socket() - { - close(); - } - - /** - * Access the implementation. - * - * @return the implementation - * @warning use this function with care - */ - inline const Protocol &protocol() const noexcept - { - return m_proto; - } - - /** - * Overloaded function. - * - * @return the implementation - */ - inline Protocol &protocol() noexcept - { - return m_proto; - } - - /** - * 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 net::Error on errors - */ - template <typename Argument> - 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(Error::System, "set"); - } - - /** - * Object-oriented option setter. - * - * The object must have `set(Socket<Address, Protocol> &) const`. - * - * @pre isOpen() - * @param option the option - * @throw net::Error on errors - */ - template <typename Option> - 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 - * @throw net::Error on errors - */ - template <typename Argument> - 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(Error::System, "get"); - - std::memcpy(&result, &desired, size); - - return result; - } - - /** - * Object-oriented option getter. - * - * The object must have `T get(Socket<Address, Protocol> &) 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 net::Error on errors - */ - template <typename Option> - inline auto get() -> decltype(std::declval<Option>().get(*this)) - { - assert(m_handle != Invalid); - - return Option().get(*this); - } - - /** - * Get the native handle. - * - * @return the handle - * @warning Not portable - */ - inline Handle handle() const noexcept - { - return m_handle; - } - - /** - * Bind using a native address. - * - * @pre isOpen() - * @param address the address - * @param length the size - * @throw net::Error on errors - */ - inline void bind(const sockaddr *address, socklen_t length) - { - assert(m_handle != Invalid); - - if (::bind(m_handle, address, length) == Failure) - throw Error(Error::System, "bind"); - } - - /** - * Overload that takes an address. - * - * @pre isOpen() - * @param address the address - * @throw net::Error on errors - */ - inline void bind(const Address &address) - { - assert(m_handle != Invalid); - - if (::bind(m_handle, address.address(), address.length()) == Failure) - throw Error(Error::System, "bind"); - } - - /** - * Listen for pending connection. - * - * @pre isOpen() - * @param max the maximum number - * @throw net::Error on errors - */ - inline void listen(int max = 128) - { - assert(m_handle != Invalid); - - if (::listen(this->m_handle, max) == Failure) - throw Error(Error::System, "listen"); - } - - /** - * Get the local name. This is a wrapper of getsockname(). - * - * @pre isOpen() - * @return the address - * @throw Error on failures - * @pre state() must not be State::Closed - */ - Address address() const - { - assert(m_handle != Invalid); - - sockaddr_storage ss; - socklen_t length = sizeof (sockaddr_storage); - - if (::getsockname(m_handle, reinterpret_cast<sockaddr *>(&ss), &length) == Failure) - throw Error(Error::System, "getsockname"); - - return Address(&ss, length); - } - - /** - * Initialize connection to the given address. - * - * @pre isOpen() - * @param address the address - * @param length the address length - * @param cond the condition - * @throw net::Error on failures - */ - void connect(const sockaddr *address, socklen_t length, Condition &cond) - { - assert(m_handle != Invalid); - - cond = Condition::None; - - m_proto.connect(*this, address, length, cond); - } - - /** - * Overloaded function. - * - * @pre isOpen() - * @param address the address - * @param length the address length - * @throw net::Error on failures - */ - inline void connect(const sockaddr *address, socklen_t length) - { - Condition dummy; - - connect(address, length, dummy); - } - - /** - * Overloaded function. - * - * @pre isOpen() - * @param address the address - * @param cond the condition - * @throw net::Error on failures - */ - inline void connect(const Address &address, Condition &cond) - { - connect(address.address(), address.length(), cond); - } - - /** - * Overloaded function. - * - * @pre isOpen() - * @param address the address - * @throw net::Error on failures - */ - inline void connect(const Address &address) - { - Condition dummy; - - connect(address.address(), address.length(), dummy); - } - - void resumeConnect(Condition &cond) - { - assert(m_handle != Invalid); - - cond = Condition::None; - - m_proto.connect(*this, cond); - } - - inline void resumeConnect() - { - Condition dummy; - - connect(dummy); - } - - Socket<Address, Protocol> accept(Address &address, Condition &cond) - { - assert(m_handle != Invalid); - - sockaddr_storage storage; - socklen_t length = sizeof (storage); - - cond = Condition::None; - - Socket<Address, Protocol> client = m_proto.accept(*this, reinterpret_cast<sockaddr *>(&storage), &length, cond); - - address = Address(&storage, length); - - return client; - } - - inline Socket<Address, Protocol> accept(Address &address) - { - Condition dummy; - - return accept(address, dummy); - } - - inline Socket<Address, Protocol> accept() - { - Address da; - Condition dc; - - return accept(da, dc); - } - - inline void resumeAccept(Condition &condition) - { - assert(m_handle != Invalid); - - m_proto.resumeAccept(*this, condition); - } - - inline void resumeAccept() - { - Condition dummy; - - resumeAccept(*this, dummy); - } - - unsigned recv(void *data, unsigned length, Condition &cond) - { - assert(m_handle != Invalid); - - cond = Condition::None; - - return m_proto.recv(*this, data, length, cond); - } - - inline unsigned recv(void *data, unsigned length) - { - Condition dummy; - - return recv(data, length, dummy); - } - - std::string recv(unsigned count, Condition &cond) - { - assert(m_handle != Invalid); - - std::string result; - - result.resize(count); - auto n = recv(const_cast<char *>(result.data()), count, cond); - result.resize(n); - - return result; - } - - inline std::string recv(unsigned count) - { - Condition dummy; - - return recv(count, dummy); - } - - unsigned send(const void *data, unsigned length, Condition &cond) - { - assert(m_handle != Invalid); - - cond = Condition::None; - - return m_proto.send(*this, data, length, cond); - } - - inline unsigned send(const void *data, unsigned length) - { - Condition dummy; - - return send(data, length, dummy); - } - - inline unsigned send(const std::string &data, Condition &cond) - { - return send(data.c_str(), data.length(), cond); - } - - inline unsigned send(const std::string &data) - { - Condition dummy; - - return send(data.c_str(), data.length(), dummy); - } - - unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen, Condition &cond) - { - assert(m_handle != Invalid); - - cond = Condition::None; - - return m_proto.sendto(*this, data, length, address, addrlen, cond); - } - - inline unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen) - { - Condition dummy; - - return send(data, length, address, addrlen, dummy); - } - - inline unsigned sendto(const void *data, unsigned length, const Address &address, Condition &cond) - { - return sendto(data, length, address.address(), address.length(), cond); - } - - inline unsigned sendto(const void *data, unsigned length, const Address &address) - { - Condition dummy; - - return sendto(data, length, address.address(), address.length(), dummy); - } - - inline unsigned sendto(const std::string &data, const Address &address, Condition &cond) - { - return sendto(data.c_str(), data.length(), address.address(), address.length(), cond); - } - - inline unsigned sendto(const std::string &data, const Address &address) - { - Condition dummy; - - return sendto(data.c_str(), data.length(), address.address(), address.length(), dummy); - } - - unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen, Condition &cond) - { - assert(m_handle != Invalid); - - cond = Condition::None; - - return m_proto.recvfrom(*this, data, length, address, addrlen, cond); - } - - inline unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen) - { - Condition dummy; - - return recvfrom(data, length, address, addrlen, dummy); - } - - unsigned recvfrom(void *data, unsigned length, Address &address, Condition &cond) - { - sockaddr_storage storage; - socklen_t addrlen = sizeof (sockaddr_storage); - - auto n = recvfrom(data, length, reinterpret_cast<sockaddr *>(&storage), &addrlen, cond); - - if (n != 0) - address = Address(&storage, addrlen); - - return n; - } - - inline unsigned recvfrom(void *data, unsigned length, Address &address) - { - Condition dummy; - - return recvfrom(data, length, address, dummy); - } - - inline unsigned recvfrom(void *data, unsigned length) - { - Address da; - Condition dc; - - return recvfrom(data, length, da, dc); - } - - std::string recvfrom(unsigned count, Address &info, Condition &cond) - { - std::string result; - - result.resize(count); - auto n = recvfrom(const_cast<char *>(result.data()), count, info, cond); - result.resize(n); - - return result; - } - - inline std::string recvfrom(unsigned count, Address &info) - { - Condition dummy; - - return recvfrom(count, info, dummy); - } - - inline std::string recvfrom(unsigned count) - { - Address da; - Condition dc; - - return recvfrom(count, da, dc); - } - - /** - * Close the socket. - * - * Automatically called from the destructor. - */ - void close() - { - if (m_handle != Invalid) { -#if defined(_WIN32) - ::closesocket(m_handle); -#else - ::close(m_handle); -#endif - m_handle = Invalid; - } - } - - /** - * Assignment operator forbidden. - * - * @return *this - */ - Socket &operator=(const Socket &) = delete; - - /** - * Transfer ownership from other to this. The other socket is left - * invalid and will not be closed. - * - * @param other the other socket - * @return this - */ - Socket &operator=(Socket &&other) noexcept - { - m_handle = other.m_handle; - m_proto = std::move(other.m_proto); - - /* Invalidate other */ - other.m_handle = Invalid; - - return *this; - } -}; - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if they equals - */ -template <typename Address, typename Protocol> -bool operator==(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() == s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if they are different - */ -template <typename Address, typename Protocol> -bool operator!=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() != s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if s1 < s2 - */ -template <typename Address, typename Protocol> -bool operator<(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() < s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if s1 > s2 - */ -template <typename Address, typename Protocol> -bool operator>(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() > s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if s1 <= s2 - */ -template <typename Address, typename Protocol> -bool operator<=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() <= s2.handle(); -} - -/** - * Compare two sockets. - * - * @param s1 the first socket - * @param s2 the second socket - * @return true if s1 >= s2 - */ -template <typename Address, typename Protocol> -bool operator>=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2) -{ - return s1.handle() >= s2.handle(); -} - -/* - * Predefined protocols - * ------------------------------------------------------------------ - */ - -namespace protocol { - -/** - * @class Tcp - * @brief Clear TCP implementation. - * - * This is the basic TCP protocol that implements recv, send, connect and accept as wrappers of the usual - * C functions. - */ -class Tcp { -public: - /** - * Socket type. - * - * @return SOCK_STREAM - */ - inline int type() const noexcept - { - return SOCK_STREAM; - } - - /** - * Do nothing. - * - * This function is just present for compatibility, it should never be called. - */ - template <typename Address> - inline void create(Socket<Address, Tcp> &) const noexcept - { - /* No-op */ - } - - /** - * Initiate connection. - * - * @param sc the socket - * @param address the address - * @param cond the condition - */ - template <typename Address, typename Protocol> - void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length, Condition &cond) - { - if (::connect(sc.handle(), address, length) == Failure) { - /* - * Determine if the error comes from a non-blocking connect that cannot be - * accomplished yet. - */ -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) - cond = Condition::Writable; - else - throw Error(Error::System, "connect", error); -#else - if (errno == EINPROGRESS) - cond = Condition::Writable; - else - throw Error(Error::System, "connect"); -#endif - } - } - - /** - * Resume the connection. - * - * Just check for SOL_SOCKET/SO_ERROR. - * - * @param sc the socket - */ - template <typename Address, typename Protocol> - void resumeConnect(Socket<Address, Protocol> &sc, Condition &) - { - int error = sc.template get<int>(SOL_SOCKET, SO_ERROR); - - if (error != 0) - throw Error(Error::System, "connect", error); - } - - /** - * Accept a new client. - * - * If there are no pending connection, an invalid socket is returned, condition is left to Condition::None. - * - * @param sc the socket - * @param address the address - * @param length the length - * @return the new socket - */ - template <typename Address, typename Protocol> - Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length, Condition &) - { - Handle handle = ::accept(sc.handle(), address, length); - - if (handle == Invalid) - return Socket<Address, Protocol>(); - - return Socket<Address, Protocol>(handle); - } - - /** - * Resume accept process. - * - * No-op for TCP. - */ - template <typename Address, typename Protocol> - inline void accept(Socket<Address, Protocol> &, Condition &) const noexcept - { - /* no op */ - } - - /** - * Receive some data. - * - * @param sc the socket - * @param data the destination buffer - * @param length the buffer length - * @param cond the condition - */ - template <typename Address> - unsigned recv(Socket<Address, Tcp> &sc, void *data, unsigned length, Condition &cond) - { - int nbread = ::recv(sc.handle(), (Arg)data, length, 0); - - if (nbread == Failure) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - nbread = 0; - cond = Condition::Readable; - } else { - throw Error(Error::System, "recv", error); - } -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) { - nbread = 0; - cond = Condition::Readable; - } else { - throw Error(Error::System, "recv"); - } -#endif - } - - return static_cast<unsigned>(nbread); - } - - /** - * Send some data. - * - * @param sc the socket - * @param data the data to send - * @param length the length - * @param cond the condition - */ - template <typename Address> - unsigned send(Socket<Address, Tcp> &sc, const void *data, unsigned length, Condition &cond) - { - int nbsent = ::send(sc.handle(), (ConstArg)data, length, 0); - - if (nbsent == Failure) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - nbsent = 0; - cond = Condition::Writable; - } else { - throw Error(Error::System, "send", error); - } -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) { - nbsent = 0; - cond = Condition::Writable; - } else { - throw Error(Error::System, "send"); - } -#endif - } - - return static_cast<unsigned>(nbsent); - } -}; - -/** - * @class Udp - * @brief Clear UDP type. - * - * This class is the basic implementation of UDP sockets. - */ -class Udp { -public: - /** - * Socket type. - * - * @return SOCK_DGRAM - */ - inline int type() const noexcept - { - return SOCK_DGRAM; - } - - /** - * Do nothing. - */ - template <typename Address, typename Protocol> - inline void create(Socket<Address, Protocol> &) noexcept - { - /* No-op */ - } - - /** - * Receive some data. - * - * @param sc the socket - * @param data the data - * @param length the length - * @param address the source address - * @param addrlen the source address in/out length - * @param cond the condition - * @return the number of bytes received - */ - template <typename Address, typename Protocol> - unsigned recvfrom(Socket<Address, Protocol> &sc, void *data, unsigned length, sockaddr *address, socklen_t *addrlen, Condition &cond) - { - int nbread; - - nbread = ::recvfrom(sc.handle(), (Arg)data, length, 0, address, addrlen); - - if (nbread == Failure) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - nbread = 0; - cond = Condition::Writable; - } else { - throw Error(Error::System, "recvfrom"); - } -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) { - nbread = 0; - cond = Condition::Writable; - } else { - throw Error(Error::System, "recvfrom"); - } -#endif - } - - return static_cast<unsigned>(nbread); - } - - /** - * Send some data. - * - * @param sc the socket - * @param data the data to send - * @param length the data length - * @param address the destination address - * @param addrlen the destination address length - * @param cond the condition - */ - template <typename Address, typename Protocol> - unsigned sendto(Socket<Address, Protocol> &sc, const void *data, unsigned length, const sockaddr *address, socklen_t addrlen, Condition &cond) - { - int nbsent; - - nbsent = ::sendto(sc.handle(), (ConstArg)data, length, 0, address, addrlen); - if (nbsent == Failure) { -#if defined(_WIN32) - int error = WSAGetLastError(); - - if (error == WSAEWOULDBLOCK) { - nbsent = 0; - cond = Condition::Writable; - } else { - throw Error(Error::System, "sendto", error); - } -#else - if (errno == EAGAIN || errno == EWOULDBLOCK) { - nbsent = 0; - cond = Condition::Writable; - } else { - throw Error(Error::System, "sendto"); - } -#endif - } - - return static_cast<unsigned>(nbsent); - } -}; - -#if !defined(NET_NO_SSL) - - -class Tls : private Tcp { -private: - using Context = std::shared_ptr<SSL_CTX>; - using Ssl = std::unique_ptr<SSL, void (*)(SSL *)>; - - /* OpenSSL objects */ - Context m_context; - Ssl m_ssl{nullptr, nullptr}; - - /* Status */ - bool m_tcpconnected{false}; - - /* - * User definable parameters - */ - ssl::Method m_method{ssl::Tlsv1}; - std::string m_key; - std::string m_certificate; - bool m_verify{false}; - - /* - * Construct with a context and ssl, for Tls::accept. - */ - Tls(Context context, Ssl ssl) - : m_context(std::move(context)) - , m_ssl(std::move(ssl)) - { - } - - /* - * Get the OpenSSL error message. - */ - inline std::string error(int error) - { - auto msg = ERR_reason_error_string(error); - - return msg == nullptr ? "" : msg; - } - - template <typename Function> - void wrap(const std::string &func, Condition &cond, Function &&function) - { - auto ret = function(); - - if (ret <= 0) { - int no = SSL_get_error(m_ssl.get(), ret); - - switch (no) { - case SSL_ERROR_WANT_READ: - cond = Condition::Readable; - break; - case SSL_ERROR_WANT_WRITE: - cond = Condition::Writable; - break; - default: - throw Error(Error::System, func, error(no)); - } - } - } - - template <typename Address, typename Protocol> - void doConnect(Socket<Address, Protocol> &sc, Condition &cond) - { - wrap("connect", cond, [&] () -> int { - return SSL_connect(m_ssl.get()); - }); - } - - template <typename Address, typename Protocol> - void doAccept(Socket<Address, Protocol> &sc, Condition &cond) - { - wrap("accept", cond, [&] () -> int { - return SSL_accept(m_ssl.get()); - }); - } - -public: - /** - * @copydoc Tcp::type - */ - inline int type() const noexcept - { - return SOCK_STREAM; - } - - /** - * Empty TLS constructor. - */ - Tls() - { -#if !defined(NET_NO_SSL_AUTO_INIT) - ::net::ssl::init(); -#endif - } - - /** - * Set the method. - * - * @param method the method - * @pre the socket must not be already created - */ - inline void setMethod(ssl::Method method) noexcept - { - assert(!m_context); - assert(!m_ssl); - - m_method = method; - } - - /** - * Use the specified private key file. - * - * @param file the path to the private key - */ - inline void setPrivateKey(std::string file) noexcept - { - m_key = std::move(file); - } - - /** - * Use the specified certificate file. - * - * @param file the path to the file - */ - inline void setCertificate(std::string file) noexcept - { - m_certificate = std::move(file); - } - - /** - * Set to true if we must verify the certificate and private key. - * - * @param verify the mode - */ - inline void setVerify(bool verify = true) noexcept - { - m_verify = verify; - } - - /** - * Initialize the SSL objects after have created. - * - * @param sc the socket - * @throw net::Error on errors - */ - template <typename Address> - inline void create(Socket<Address, Tls> &sc) - { - auto method = (m_method == ssl::Tlsv1) ? TLSv1_method() : SSLv3_method(); - - m_context = Context(SSL_CTX_new(method), SSL_CTX_free); - m_ssl = Ssl(SSL_new(m_context.get()), SSL_free); - - SSL_set_fd(m_ssl.get(), sc.handle()); - - /* Load certificates */ - if (m_certificate.size() > 0) - SSL_CTX_use_certificate_file(m_context.get(), m_certificate.c_str(), SSL_FILETYPE_PEM); - if (m_key.size() > 0) - SSL_CTX_use_PrivateKey_file(m_context.get(), m_key.c_str(), SSL_FILETYPE_PEM); - if (m_verify && !SSL_CTX_check_private_key(m_context.get())) - throw Error(Error::System, "(openssl)", "unable to verify key"); - } - - template <typename Address, typename Protocol> - void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length, Condition &cond) - { - /* 1. Connect using raw TCP */ - Tcp::connect(sc, address, length, cond); - - /* 2. If the connection is complete (e.g. non-blocking), try handshake */ - if (cond == Condition::None) { - m_tcpconnected = true; - doConnect(sc, cond); - } - } - - template <typename Address, typename Protocol> - void connect(Socket<Address, Protocol> &sc, Condition &cond) - { - /* 1. Be sure to complete standard connect before */ - if (!m_tcpconnected) { - Tcp::connect(sc, cond); - m_tcpconnected = cond = Condition::None; - } - - if (m_tcpconnected) - doConnect(sc, cond); - } - - template <typename Address> - void accept(Socket<Address, Tls> &sc, Socket<Address, Tls> &client, sockaddr *address, socklen_t *length, Condition &cond) - { - /* TCP sets empty client if no pending connection is available */ - Tcp::accept(sc, client, address, length, cond); - - if (client.isOpen()) { - Tls &proto = client.protocol(); - - /* 1. Share the context */ - proto.m_context = m_context; - - /* 2. Create new SSL instance */ - proto.m_ssl = Ssl(SSL_new(m_context.get()), SSL_free); - - SSL_set_fd(proto.m_ssl.get(), client.handle()); - - /* 3. Try accept process on the **new** client */ - proto.doAccept(client, cond); - } - } - - template <typename Address, typename Protocol> - inline void accept(Socket<Address, Protocol> &sc, Condition &cond) - { - doAccept(sc, cond); - } - - - template <typename Address> - unsigned recv(Socket<Address, Tls> &sc, void *data, unsigned len, Condition &cond) - { - int nbread = 0; - - wrap("recv", cond, [&] () -> int { - return (nbread = SSL_read(m_ssl.get(), data, len)); - }); - - return static_cast<unsigned>(nbread < 0 ? 0 : nbread); - } - - template <typename Address> - unsigned send(Socket<Address, Tls> &sc, const void *data, unsigned len, Condition &cond) - { - int nbsent = 0; - - wrap("send", cond, [&] () -> int { - return (nbsent = SSL_write(m_ssl.get(), data, len)); - }); - - return static_cast<unsigned>(nbsent < 0 ? 0 : nbsent); - } -}; - -#endif // !NET_NO_SSL - -} // !protocol - -/* - * Predefined addresses - * ------------------------------------------------------------------ - */ - -namespace address { - -class Ip { -private: - union { - sockaddr_in6 m_sin6; - sockaddr_in m_sin; - }; - - int m_domain; - -public: - inline Ip(int domain = AF_INET) noexcept - : m_domain(domain) - { - assert(domain == AF_INET || domain == AF_INET6); - - std::memset(&m_sin, 0, sizeof (sockaddr_in)); - } - - inline Ip(const std::string &host, std::uint16_t port, int domain) - : Ip(domain) - { - if (m_domain == AF_INET) - make(host, port, m_sin); - else - make(host, port, m_sin6); - } - - inline Ip(const sockaddr_storage *ss, socklen_t length) noexcept - : Ip(ss->ss_family) - { - assert(ss->ss_family == AF_INET || ss->ss_family == AF_INET6); - - if (ss->ss_family == AF_INET) - std::memcpy(&m_sin, ss, length); - else - std::memcpy(&m_sin6, ss, length); - } - - inline int domain() const noexcept - { - return AF_INET; - } - - inline const sockaddr *address() const noexcept - { - return m_domain == AF_INET ? reinterpret_cast<const sockaddr *>(&m_sin) : reinterpret_cast<const sockaddr *>(&m_sin6); - } - - inline socklen_t length() const noexcept - { - return m_domain == AF_INET ? sizeof (sockaddr_in) : sizeof (sockaddr_in6); - } - - inline std::uint16_t port() const noexcept - { - return m_domain == AF_INET ? ntohs(m_sin.sin_port) : ntohs(m_sin6.sin6_port); - } - - inline std::string ip() const - { - return m_domain == AF_INET ? ip(m_sin) : ip(m_sin6); - } - - static void make(const std::string &host, std::uint16_t port, sockaddr_in &sin) - { -#if !defined(NET_NO_AUTO_INIT) - net::init(); -#endif - - sin.sin_family = AF_INET; - sin.sin_port = htons(port); - - if (host == "*") - sin.sin_addr.s_addr = INADDR_ANY; -#if defined(NET_HAVE_INET_PTON) - else if (inet_pton(AF_INET, host.c_str(), &sin.sin_addr) <= 0) - throw Error(Error::System, "inet_pton"); -#else - else - throw Error(Error::System, "inet_pton", std::strerror(ENOSYS)); -#endif - } - - static void make(const std::string &address, std::uint16_t port, sockaddr_in6 &sin6) - { -#if !defined(NET_NO_AUTO_INIT) - net::init(); -#endif - - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(port); - - if (address == "*") - sin6.sin6_addr = in6addr_any; -#if defined(NET_HAVE_INET_PTON) - else if (inet_pton(AF_INET6, address.c_str(), &sin6.sin6_addr) <= 0) - throw Error(Error::System, "inet_pton"); -#else - else - throw Error(Error::System, "inet_pton", std::strerror(ENOSYS)); -#endif - } - - static std::string ip(const sockaddr_in &sin) - { -#if !defined(NET_NO_AUTO_INIT) - net::init(); -#endif - -#if !defined(NET_HAVE_INET_NTOP) - throw Error(Error::System, "inet_ntop", std::strerror(ENOSYS)); -#else - char result[INET_ADDRSTRLEN + 1]; - - std::memset(result, 0, sizeof (result)); - - if (!inet_ntop(AF_INET, const_cast<in_addr *>(&sin.sin_addr), result, sizeof (result))) - throw Error(Error::System, "inet_ntop"); - - return result; -#endif - } - - static std::string ip(const sockaddr_in6 &sin6) - { -#if !defined(NET_NO_AUTO_INIT) - net::init(); -#endif - -#if !defined(NET_HAVE_INET_NTOP) - throw Error(Error::System, "inet_ntop", std::strerror(ENOSYS)); -#else - char result[INET6_ADDRSTRLEN]; - - std::memset(result, 0, sizeof (result)); - - if (!inet_ntop(AF_INET6, const_cast<in6_addr *>(&sin6.sin6_addr), result, sizeof (result))) - throw Error(Error::System, "inet_ntop"); - - return result; -#endif - } -}; - -class Ipv4 { -private: - sockaddr_in m_sin; - -public: - inline Ipv4() noexcept - { - std::memset(&m_sin, 0, sizeof (sockaddr_in)); - } - - inline Ipv4(const std::string &host, std::uint16_t port) - : Ipv4() - { - Ip::make(host, port, m_sin); - } - - inline Ipv4(const sockaddr_storage *ss, socklen_t length) noexcept - { - std::memcpy(&m_sin, ss, length); - } - - inline int domain() const noexcept - { - return AF_INET; - } - - inline const sockaddr *address() const noexcept - { - return reinterpret_cast<const sockaddr *>(&m_sin); - } - - inline socklen_t length() const noexcept - { - return sizeof (sockaddr_in); - } - - inline std::uint16_t port() const noexcept - { - return ntohs(m_sin.sin_port); - } - - inline std::string ip() const - { - return Ip::ip(m_sin); - } -}; - -class Ipv6 { -private: - sockaddr_in6 m_sin6; - -public: - inline Ipv6() noexcept - { - std::memset(&m_sin6, 0, sizeof (sockaddr_in6)); - } - - inline Ipv6(const std::string &address, std::uint16_t port) - : Ipv6() - { - Ip::make(address, port, m_sin6); - } - - inline Ipv6(const sockaddr_storage *ss, socklen_t length) noexcept - { - std::memcpy(&m_sin6, ss, length); - } - - inline int domain() const noexcept - { - return AF_INET6; - } - - inline const sockaddr *address() const noexcept - { - return reinterpret_cast<const sockaddr *>(&m_sin6); - } - - inline socklen_t length() const noexcept - { - return sizeof (sockaddr_in6); - } - - inline std::uint16_t port() const noexcept - { - return ntohs(m_sin6.sin6_port); - } - - inline std::string ip() const - { - return Ip::ip(m_sin6); - } -}; - -#if !defined(_WIN32) - -/** - * @class Local - * @brief unix family sockets - * - * Create an address to a specific path. Only available on Unix. - */ -class Local { -private: - sockaddr_un m_sun; - std::string m_path; - -public: - /** - * Get the domain AF_LOCAL. - * - * @return AF_LOCAL - */ - inline int domain() const noexcept - { - return AF_LOCAL; - } - - /** - * Default constructor. - */ - inline Local() noexcept - { - std::memset(&m_sun, 0, sizeof (sockaddr_un)); - } - - /** - * Construct an address to a path. - * - * @param path the path - * @param rm remove the file before (default: false) - */ - Local(std::string path, bool rm = false) noexcept - : m_path(std::move(path)) - { - /* Silently remove the file even if it fails */ - if (rm) - ::remove(m_path.c_str()); - - /* Copy the path */ - std::memset(m_sun.sun_path, 0, sizeof (m_sun.sun_path)); - std::strncpy(m_sun.sun_path, m_path.c_str(), sizeof (m_sun.sun_path) - 1); - - /* Set the parameters */ - m_sun.sun_family = AF_LOCAL; - } - - /** - * Construct an unix address from a storage address. - * - * @pre storage's domain must be AF_LOCAL - * @param ss the storage - * @param length the length - */ - Local(const sockaddr_storage *ss, socklen_t length) noexcept - { - assert(ss->ss_family == AF_LOCAL); - - std::memcpy(&m_sun, ss, length); - m_path = reinterpret_cast<const sockaddr_un &>(m_sun).sun_path; - } - - /** - * Get the sockaddr_un. - * - * @return the address - */ - inline const sockaddr *address() const noexcept - { - return reinterpret_cast<const sockaddr *>(&m_sun); - } - - /** - * Get the address length. - * - * @return the length - */ - inline socklen_t length() const noexcept - { -#if defined(NET_HAVE_SUN_LEN) - return SUN_LEN(&m_sun); -#else - return sizeof (m_sun); -#endif - } -}; - -#endif // !_WIN32 - -} // !address - -/* - * Listener backends - * ------------------------------------------------------------------ - */ - -namespace backend { - -/** - * @class Select - * @brief Implements select(2) - * - * This class is the fallback of any other method, it is not preferred at all for many reasons. - */ -class Select { -public: - inline void set(const ListenerTable &, Handle, Condition, bool) noexcept - { - } - - inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept - { - } - - std::vector<ListenerStatus> wait(const ListenerTable &table, int ms) - { - timeval maxwait, *towait; - fd_set readset; - fd_set writeset; - - FD_ZERO(&readset); - FD_ZERO(&writeset); - - Handle max = 0; - - for (const auto &pair : table) { - if ((pair.second & Condition::Readable) == Condition::Readable) { - FD_SET(pair.first, &readset); - } - if ((pair.second & Condition::Writable) == Condition::Writable) { - FD_SET(pair.first, &writeset); - } - - if (pair.first > max) { - max = pair.first; - } - } - - maxwait.tv_sec = 0; - maxwait.tv_usec = ms * 1000; - - // Set to nullptr for infinite timeout. - towait = (ms < 0) ? nullptr : &maxwait; - - auto error = ::select(max + 1, &readset, &writeset, nullptr, towait); - if (error == Failure) { - throw Error{Error::System, "select"}; - } - if (error == 0) { - throw Error{Error::Timeout, "select", std::strerror(ETIMEDOUT)}; - } - - std::vector<ListenerStatus> sockets; - - for (const auto &pair : table) { - if (FD_ISSET(pair.first, &readset)) { - sockets.push_back(ListenerStatus{pair.first, Condition::Readable}); - } - if (FD_ISSET(pair.first, &writeset)) { - sockets.push_back(ListenerStatus{pair.first, Condition::Writable}); - } - } - - return sockets; - } - - inline std::string name() const noexcept - { - return "select"; - } -}; - -#if defined(NET_HAVE_KQUEUE) - -/** - * @class Kqueue - * @brief Implements kqueue(2). - * - * This implementation is available on all BSD and Mac OS X. It is better than - * poll(2) because it's O(1), however it's a bit more memory consuming. - */ -class Kqueue { -private: - std::vector<struct kevent> m_result; - int m_handle; - - Kqueue(const Kqueue &) = delete; - Kqueue &operator=(const Kqueue &) = delete; - - void update(Handle sc, int filter, int kflags) - { - struct kevent ev; - - EV_SET(&ev, h, filter, kflags, 0, 0, nullptr); - - if (kevent(m_handle, &ev, 1, nullptr, 0, nullptr) < 0) - throw Error(Error::System, "kevent"); - } - -public: - inline Kqueue() - : m_handle(kqueue()) - { - if (m_handle < 0) - throw Error(Error::System, "kqueue"); - } - - inline Kqueue(Kqueue &&other) noexcept - : m_handle(other.m_handle) - { - other.m_handle = -1; - } - - inline ~Kqueue() - { - if (m_handle != -1) - close(m_handle); - } - - inline std::string name() const noexcept - { - return "kqueue"; - } - - void set(const ListenerTable &, Handle, Condition, bool add) - { - if ((condition & Condition::Readable) == Condition::Readable) - update(h, EVFILT_READ, EV_ADD | EV_ENABLE); - if ((condition & Condition::Writable) == Condition::Writable) - update(h, EVFILT_WRITE, EV_ADD | EV_ENABLE); - - if (add) - m_result.resize(m_result.size() + 1); - } - - void unset(const ListenerTable &, Handle, Condition, bool) - { - if ((condition & Condition::Readable) == Condition::Readable) - update(h, EVFILT_READ, EV_DELETE); - if ((condition & Condition::Writable) == Condition::Writable) - update(h, EVFILT_WRITE, EV_DELETE); - - if (remove) - m_result.resize(m_result.size() - 1); - } - - std::vector<ListenerStatus> wait(const ListenerTable &, int) - { - std::vector<ListenerStatus> sockets; - timespec ts = { 0, 0 }; - timespec *pts = (ms <= 0) ? nullptr : &ts; - - ts.tv_sec = ms / 1000; - ts.tv_nsec = (ms % 1000) * 1000000; - - int nevents = kevent(m_handle, nullptr, 0, &m_result[0], m_result.capacity(), pts); - - if (nevents == 0) - throw Error(Error::Timeout, "kevent", std::strerror(ETIMEDOUT)); - if (nevents < 0) - throw Error(Error::System, "kevent"); - - for (int i = 0; i < nevents; ++i) { - sockets.push_back(ListenerStatus{ - static_cast<Handle>(m_result[i].ident), - m_result[i].filter == EVFILT_READ ? Condition::Readable : Condition::Writable - }); - } - - return sockets; - } - - inline Kqueue &operator=(Kqueue &&other) noexcept - { - m_handle = other.m_handle; - other.m_handle = -1; - - return *this; - } -}; - -#endif // !NET_HAVE_KQUEUE - -#if defined(NET_HAVE_POLL) - -#if defined(_WIN32) -# define poll WSAPoll -#endif - -/** - * @class Poll - * @brief Implements poll(2). - * - * Poll is widely supported and is better than select(2). It is still not the - * best option as selecting the sockets is O(n). - */ -class Poll { -private: - std::vector<pollfd> m_fds; - - short toPoll(Condition condition) const noexcept - { - short result = 0; - - if ((condition & Condition::Readable) == Condition::Readable) - result |= POLLIN; - if ((condition & Condition::Writable) == Condition::Writable) - result |= POLLOUT; - - return result; - } - - Condition toCondition(short &event) const noexcept - { - Condition condition = Condition::None; - - /* - * Poll implementations mark the socket differently regarding - * the disconnection of a socket. - * - * At least, even if POLLHUP or POLLIN is set, recv() always - * return 0 so we mark the socket as readable. - */ - if ((event & POLLIN) || (event & POLLHUP)) - condition |= Condition::Readable; - if (event & POLLOUT) - condition |= Condition::Writable; - - /* Reset event for safety */ - event = 0; - - return condition; - } - -public: - inline std::string name() const noexcept - { - return "poll"; - } - - void set(const ListenerTable &, Handle h, Condition condition, bool add) - { - if (add) { - m_fds.push_back(pollfd{h, toPoll(condition), 0}); - } else { - auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) { - return pfd.fd == h; - }); - - it->events |= toPoll(condition); - } - } - - void unset(const ListenerTable &, Handle h, Condition condition, bool remove) - { - auto it = std::find_if(m_fds.begin(), m_fds.end(), [&] (const pollfd &pfd) { - return pfd.fd == h; - }); - - if (remove) - m_fds.erase(it); - else - it->events &= ~(toPoll(condition)); - } - - std::vector<ListenerStatus> wait(const ListenerTable &, int ms) - { - auto result = poll(m_fds.data(), m_fds.size(), ms); - - if (result == 0) - throw Error{Error::Timeout, "select", std::strerror(ETIMEDOUT)}; - if (result < 0) - throw Error{Error::System, "poll"}; - - std::vector<ListenerStatus> sockets; - for (auto &fd : m_fds) - if (fd.revents != 0) - sockets.push_back(ListenerStatus{fd.fd, toCondition(fd.revents)}); - - return sockets; - } -}; - -#endif // !NET_HAVE_POLL - -#if defined(NET_HAVE_EPOLL) - -/** - * @class Epoll - * @brief Linux's epoll. - */ -class Epoll { -private: - int m_handle{-1}; - std::vector<epoll_event> m_events; - - Epoll(const Epoll &) = delete; - Epoll &operator=(const Epoll &) = delete; - - std::uint32_t toEpoll(Condition flags) const noexcept - { - std::uint32_t events = 0; - - if ((condition & Condition::Readable) == Condition::Readable) - events |= EPOLLIN; - if ((condition & Condition::Writable) == Condition::Writable) - events |= EPOLLOUT; - - return events; - } - - Condition toCondition(std::uint32_t events) const noexcept - { - Condition condition = Condition::None; - - if ((events & EPOLLIN) || (events & EPOLLHUP)) - condition |= Condition::Readable; - if (events & EPOLLOUT) - condition |= Condition::Writable; - - return condition; - } - - void update(Handle sc, int op, int eflags) - { - epoll_event ev; - - std::memset(&ev, 0, sizeof (epoll_event)); - - ev.events = eflags; - ev.data.fd = h; - - if (epoll_ctl(m_handle, op, h, &ev) < 0) - throw Error(Error::System, "epoll_ctl"); - } - -public: - /** - * Construct the epoll instance. - */ - inline Epoll() - : m_handle(epoll_create1(0)) - { - if (m_handle < 0) - throw Error{Error::System, "epoll_create"}; - } - - inline Epoll(Epoll &&other) noexcept - : m_handle(other.m_handle) - { - other.m_handle = -1; - } - - inline ~Epoll() - { - if (m_handle != -1) - close(m_handle); - } - - inline std::string name() const noexcept - { - return "epoll"; - } - - /* - * For set and unset, we need to apply the whole flags required, so if the socket - * was set to Connection::Readable and user add Connection::Writable, we must - * place both. - */ - void set(const ListenerTable &, Handle, Condition, bool add) - { - if (add) { - update(sc, EPOLL_CTL_ADD, toEpoll(condition)); - m_events.resize(m_events.size() + 1); - } else { - update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) | condition)); - } - } - - /* - * Unset is a bit complicated case because Listener tells us which - * flag to remove but to update epoll descriptor we need to pass - * the effective flags that we want to be applied. - * - * So we put the same flags that are currently effective and remove the - * requested one. - */ - void unset(const ListenerTable &, Handle, Condition, bool remove) - { - if (remove) { - update(sc, EPOLL_CTL_DEL, 0); - m_events.resize(m_events.size() - 1); - } else { - update(sc, EPOLL_CTL_MOD, toEpoll(table.at(sc) & ~(condition))); - } - } - - std::vector<ListenerStatus> wait(const ListenerTable &, int ms) - { - int ret = epoll_wait(m_handle, m_events.data(), m_events.size(), ms); - std::vector<ListenerStatus> result; - - if (ret == 0) - throw Error(Error::Timeout, "epoll_wait", std::strerror(ETIMEDOUT)); - if (ret < 0) - throw Error(Error::System, "epoll_wait"); - - for (int i = 0; i < ret; ++i) - result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)}); - - return result; - } - - inline Epoll &operator=(Epoll &&other) - { - m_handle = other.m_handle; - other.m_handle = -1; - - return *this; - } -}; - -#endif // !NET_HAVE_EPOLL - -} // !backend - -/* - * Predefined options - * ------------------------------------------------------------------ - */ - -namespace option { - -/** - * @class SockBlockMode - * @brief Set or get the blocking-mode for a socket. - * @warning On Windows, it's not possible to check if the socket is blocking or not. - */ -class SockBlockMode { -private: - bool m_value; - -public: - inline SockBlockMode(bool value = true) noexcept - : m_value(value) - { - } - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - void set(Socket<Address, Protocol> &sc) const - { -#if defined(O_NONBLOCK) && !defined(_WIN32) - int flags; - - if ((flags = fcntl(sc.handle(), F_GETFL, 0)) < 0) - flags = 0; - - if (m_value) - flags &= ~(O_NONBLOCK); - else - flags |= O_NONBLOCK; - - if (fcntl(sc.handle(), F_SETFL, flags) < 0) - throw Error(Error::System, "fcntl"); -#else - unsigned long flags = (m_value) ? 0 : 1; - - if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure) - throw Error(Error::System, "fcntl"); -#endif - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - bool get(Socket<Address, Protocol> &sc) const - { -#if defined(O_NONBLOCK) && !defined(_WIN32) - int flags = fcntl(sc.handle(), F_GETFL, 0); - - if (flags < 0) - throw Error(Error::System, "fcntl"); - - return !(flags & O_NONBLOCK); -#else - throw Error(Error::Other, "get", std::strerror(ENOSYS)); -#endif - } -}; - -/** - * @class SockReceiveBuffer - * @brief Set or get the input buffer. - */ -class SockReceiveBuffer { -public: - /** - * Set to the buffer size. - */ - int value{2048}; - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(SOL_SOCKET, SO_RCVBUF, value); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline int get(Socket<Address, Protocol> &sc) const - { - return sc.template get<int>(SOL_SOCKET, SO_RCVBUF); - } -}; - -/** - * @class SockReuseAddress - * @brief Reuse address, must be used before calling Socket::bind - */ -class SockReuseAddress { -private: - bool m_value; - -public: - inline SockReuseAddress(bool value = true) noexcept - : m_value(value) - { - } - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(SOL_SOCKET, SO_REUSEADDR, m_value ? 1 : 0); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline bool get(Socket<Address, Protocol> &sc) const - { - return static_cast<bool>(sc.template get<int>(SOL_SOCKET, SO_REUSEADDR)); - } -}; - -/** - * @class SockSendBuffer - * @brief Set or get the output buffer. - */ -class SockSendBuffer { -public: - /** - * Set to the buffer size. - */ - int value{2048}; - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(SOL_SOCKET, SO_SNDBUF, value); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline int get(Socket<Address, Protocol> &sc) const - { - return sc.template get<int>(SOL_SOCKET, SO_SNDBUF); - } -}; - -/** - * @class TcpNoDelay - * @brief Set this option if you want to disable nagle's algorithm. - */ -class TcpNoDelay { -private: - bool m_value; - -public: - inline TcpNoDelay(bool value = true) noexcept - : m_value(value) - { - } - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(IPPROTO_TCP, TCP_NODELAY, m_value ? 1 : 0); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline bool get(Socket<Address, Protocol> &sc) const - { - return static_cast<bool>(sc.template get<int>(IPPROTO_TCP, TCP_NODELAY)); - } -}; - -/** - * @class Ipv6Only - * @brief Control IPPROTO_IPV6/IPV6_V6ONLY - * - * Note: some systems may or not set this option by default so it's a good idea to set it in any case to either - * false or true if portability is a concern. - */ -class Ipv6Only { -private: - bool m_value; - -public: - inline Ipv6Only(bool value = true) noexcept - : m_value(value) - { - } - - /** - * Set the option. - * - * @param sc the socket - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline void set(Socket<Address, Protocol> &sc) const - { - sc.set(IPPROTO_IPV6, IPV6_V6ONLY, m_value ? 1 : 0); - } - - /** - * Get the option. - * - * @return the value - * @throw Error on errors - */ - template <typename Address, typename Protocol> - inline bool get(Socket<Address, Protocol> &sc) const - { - return static_cast<bool>(sc.template get<int>(IPPROTO_IPV6, IPV6_V6ONLY)); - } -}; - -} // !option - -/** - * @class Listener - * @brief Synchronous multiplexing - * - * Convenient wrapper around the select() system call. - * - * This class is implemented using a bridge pattern to allow different uses - * of listener implementation. - * - * You should not reinstanciate a new Listener at each iteartion of your - * main loop as it can be extremely costly. Instead use the same listener that - * you can safely modify on the fly. - * - * Currently, poll, epoll, select and kqueue are available. - * - * To implement the backend, the following functions must be available: - * - * ### Set - * - * @code - * void set(const ListenerTable &, Handle sc, Condition condition, bool add); - * @endcode - * - * This function, takes the socket to be added and the flags. The condition is - * always guaranteed to be correct and the function will never be called twice - * even if the user tries to set the same flag again. - * - * An optional add argument is added for backends which needs to do different - * operation depending if the socket was already set before or if it is the - * first time (e.g EPOLL_CTL_ADD vs EPOLL_CTL_MOD for epoll(7). - * - * ### Unset - * - * @code - * void unset(const ListenerTable &, Handle sc, Condition condition, bool remove); - * @endcode - * - * Like set, this function is only called if the condition is actually set and will - * not be called multiple times. - * - * Also like set, an optional remove argument is set if the socket is being - * completely removed (e.g no more flags are set for this socket). - * - * ### Wait - * - * @code - * std::vector<ListenerStatus> wait(const ListenerTable &, int ms); - * @endcode - * - * Wait for the sockets to be ready with the specified milliseconds. Must return a list of ListenerStatus, - * may throw any exceptions. - * - * ### Name - * - * @code - * inline const char *name() const noexcept - * @endcode - * - * Returns the backend name. Usually the class in lower case. - */ -template <typename Backend = backend :: NET_DEFAULT_BACKEND> -class Listener { -private: - Backend m_backend; - ListenerTable m_table; - -public: - /** - * Construct an empty listener. - */ - Listener() = default; - - /** - * Get the backend. - * - * @return the backend - */ - inline const Backend &backend() const noexcept - { - return m_backend; - } - - /** - * Get the non-modifiable table. - * - * @return the table - */ - inline const ListenerTable &table() const noexcept - { - return m_table; - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline ListenerTable::const_iterator begin() const noexcept - { - return m_table.begin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline ListenerTable::const_iterator cbegin() const noexcept - { - return m_table.cbegin(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline ListenerTable::const_iterator end() const noexcept - { - return m_table.end(); - } - - /** - * Overloaded function. - * - * @return the iterator - */ - inline ListenerTable::const_iterator cend() const noexcept - { - return m_table.cend(); - } - - /** - * Add or update a socket to the listener. - * - * If the socket is already placed with the appropriate flags, the - * function is a no-op. - * - * If incorrect flags are passed, the function does nothing. - * - * @param sc the socket - * @param condition the condition (may be OR'ed) - * @throw Error if the backend failed to set - */ - void set(Handle sc, Condition condition) - { - /* Invalid or useless flags */ - if (condition == Condition::None || static_cast<int>(condition) > 0x3) - return; - - auto it = m_table.find(sc); - - /* - * Do not update the table if the backend failed to add - * or update. - */ - if (it == m_table.end()) { - m_backend.set(m_table, sc, condition, true); - m_table.emplace(sc, condition); - } else { - /* Remove flag if already present */ - if ((condition & Condition::Readable) == Condition::Readable && - (it->second & Condition::Readable) == Condition::Readable) - condition &= ~(Condition::Readable); - if ((condition & Condition::Writable) == Condition::Writable && - (it->second & Condition::Writable) == Condition::Writable) - condition &= ~(Condition::Writable); - - /* Still need a call? */ - if (condition != Condition::None) { - m_backend.set(m_table, sc, condition, false); - it->second |= condition; - } - } - } - - /** - * Unset a socket from the listener, only the flags is removed - * unless the two flagss are requested. - * - * For example, if you added a socket for both reading and writing, - * unsetting the write flags will keep the socket for reading. - * - * @param sc the socket - * @param condition the condition (may be OR'ed) - * @see remove - */ - void unset(Handle sc, Condition condition) - { - auto it = m_table.find(sc); - - /* Invalid or useless flags */ - if (condition == Condition::None || static_cast<int>(condition) > 0x3 || it == m_table.end()) - return; - - /* - * Like set, do not update if the socket is already at the appropriate - * state. - */ - if ((condition & Condition::Readable) == Condition::Readable && - (it->second & Condition::Readable) != Condition::Readable) - condition &= ~(Condition::Readable); - if ((condition & Condition::Writable) == Condition::Writable && - (it->second & Condition::Writable) != Condition::Writable) - condition &= ~(Condition::Writable); - - if (condition != Condition::None) { - /* Determine if it's a complete removal */ - bool removal = ((it->second) & ~(condition)) == Condition::None; - - m_backend.unset(m_table, sc, condition, removal); - - if (removal) - m_table.erase(it); - else - it->second &= ~(condition); - } - } - - /** - * Remove completely the socket from the listener. - * - * It is a shorthand for unset(sc, Condition::Readable | Condition::Writable); - * - * @param sc the socket - */ - inline void remove(Handle sc) - { - unset(sc, Condition::Readable | Condition::Writable); - } - - /** - * Remove all sockets. - */ - inline void clear() - { - while (!m_table.empty()) - remove(m_table.begin()->first); - } - - /** - * Get the number of sockets in the listener. - */ - inline ListenerTable::size_type size() const noexcept - { - return m_table.size(); - } - - /** - * Select a socket. Waits for a specific amount of time specified as the duration. - * - * @param duration the duration - * @return the socket ready - */ - template <typename Rep, typename Ratio> - inline ListenerStatus wait(const std::chrono::duration<Rep, Ratio> &duration) - { - auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); - - return m_backend.wait(m_table, cvt.count())[0]; - } - - /** - * Overload with milliseconds. - * - * @param timeout the optional timeout in milliseconds - * @return the socket ready - */ - inline ListenerStatus wait(int timeout = -1) - { - return wait(std::chrono::milliseconds(timeout)); - } - - /** - * Select multiple sockets. - * - * @param duration the duration - * @return the socket ready - */ - template <typename Rep, typename Ratio> - inline std::vector<ListenerStatus> waitMultiple(const std::chrono::duration<Rep, Ratio> &duration) - { - auto cvt = std::chrono::duration_cast<std::chrono::milliseconds>(duration); - - return m_backend.wait(m_table, cvt.count()); - } - - /** - * Overload with milliseconds. - * - * @return the socket ready - */ - inline std::vector<ListenerStatus> waitMultiple(int timeout = -1) - { - return waitMultiple(std::chrono::milliseconds(timeout)); - } -}; - -} // !net - -#endif // !NET_H
--- a/modules/net/test/main.cpp Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,701 +0,0 @@ -/* - * main.cpp -- test sockets - * - * Copyright (c) 2013-2015 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. - */ - -#include <chrono> -#include <iostream> -#include <sstream> -#include <string> -#include <thread> - -#include <gtest/gtest.h> - -#include <net.h> - -using namespace net; -using namespace net::address; -using namespace net::option; -using namespace net::protocol; - -using namespace std::literals::chrono_literals; - -/* - * Options - * ------------------------------------------------------------------ - */ - -TEST(Options, reuse) -{ - SocketTcpIp s; - - try { - s.set(option::SockReuseAddress(true)); - ASSERT_TRUE(s.get<option::SockReuseAddress>()); - - s.set(option::SockReuseAddress(false)); - ASSERT_FALSE(s.get<option::SockReuseAddress>()); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Options, nodelay) -{ - SocketTcpIp s; - - try { - s.set(option::TcpNoDelay(true)); - ASSERT_TRUE(s.get<option::TcpNoDelay>()); - - s.set(option::TcpNoDelay(false)); - ASSERT_FALSE(s.get<option::TcpNoDelay>()); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -TEST(Options, v6only) -{ - SocketTcpIpv6 s; - - try { - s.set(option::Ipv6Only(true)); - ASSERT_TRUE(s.get<option::Ipv6Only>()); - - s.set(option::Ipv6Only(false)); - ASSERT_FALSE(s.get<option::Ipv6Only>()); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } -} - -/* -------------------------------------------------------- - * TCP tests - * -------------------------------------------------------- */ - -class TcpServerTest : public testing::Test { -protected: - SocketTcp<Ipv4> m_server; - SocketTcp<Ipv4> m_client; - - std::thread m_tserver; - std::thread m_tclient; - -public: - TcpServerTest() - { - m_server.set(SockReuseAddress()); - } - - ~TcpServerTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(TcpServerTest, connect) -{ - m_tserver = std::thread([this] () { - SocketTcp<Ipv4> sc; - - m_server.bind(Ipv4("*", 16000)); - m_server.listen(); - m_server.accept(sc); - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Ipv4("127.0.0.1", 16000)); - m_client.close(); - }); -} - -TEST_F(TcpServerTest, io) -{ - m_tserver = std::thread([this] () { - m_server.bind(Ipv4("*", 16000)); - m_server.listen(); - - SocketTcp<Ipv4> client; - - m_server.accept(client); - - auto msg = client.recv(512); - - ASSERT_EQ("hello world", msg); - - client.send(msg); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Ipv4("127.0.0.1", 16000)); - m_client.send("hello world"); - - ASSERT_EQ("hello world", m_client.recv(512)); - }); -} - -/* -------------------------------------------------------- - * UDP tests - * -------------------------------------------------------- */ - -class UdpServerTest : public testing::Test { -protected: - SocketUdp<Ipv4> m_server; - SocketUdp<Ipv4> m_client; - - std::thread m_tserver; - std::thread m_tclient; - -public: - UdpServerTest() - { - m_server.set(SockBlockMode()); - } - - ~UdpServerTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(UdpServerTest, io) -{ - m_tserver = std::thread([this] () { - Ipv4 client; - Ipv4 info("*", 16000); - - m_server.bind(info); - auto msg = m_server.recvfrom(512, &client); - - ASSERT_EQ("hello world", msg); - - m_server.sendto(msg, client); - m_server.close(); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - Ipv4 info("127.0.0.1", 16000); - - m_client.sendto("hello world", info); - - ASSERT_EQ("hello world", m_client.recvfrom(512, &info)); - - m_client.close(); - }); -} - -/* -------------------------------------------------------- - * Listener: set function - * -------------------------------------------------------- */ - -class TestBackendSet { -public: - int m_callcount{0}; - bool m_added{false}; - Condition m_flags{Condition::None}; - - inline void set(const ListenerTable &, Handle, Condition flags, bool add) noexcept - { - m_callcount ++; - m_added = add; - m_flags |= flags; - } - - inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept - { - } - - std::vector<ListenerStatus> wait(const ListenerTable &, int) - { - return {}; - } -}; - -class TestBackendSetFail { -public: - inline void set(const ListenerTable &, Handle, Condition, bool) - { - throw "fail"; - } - - inline void unset(const ListenerTable &, Handle, Condition, bool) noexcept - { - } - - std::vector<ListenerStatus> wait(const ListenerTable &, int) - { - return {}; - } -}; - -TEST(ListenerSet, initialAdd) -{ - Listener<TestBackendSet> listener; - Handle s = 0; - - listener.set(s, Condition::Readable); - - ASSERT_EQ(1U, listener.size()); - ASSERT_EQ(1, listener.backend().m_callcount); - ASSERT_TRUE(listener.backend().m_added); - ASSERT_TRUE(listener.backend().m_flags == Condition::Readable); -} - -TEST(ListenerSet, readThenWrite) -{ - Listener<TestBackendSet> listener; - Handle s = 0; - - listener.set(s, Condition::Readable); - listener.set(s, Condition::Writable); - - ASSERT_EQ(1U, listener.size()); - ASSERT_EQ(2, listener.backend().m_callcount); - ASSERT_FALSE(listener.backend().m_added); - ASSERT_TRUE(static_cast<int>(listener.backend().m_flags) == 0x3); -} - -TEST(ListenerSet, allOneShot) -{ - Listener<TestBackendSet> listener; - Handle s = 0; - - listener.set(s, Condition::Readable | Condition::Writable); - - ASSERT_EQ(1U, listener.size()); - ASSERT_EQ(1, listener.backend().m_callcount); - ASSERT_TRUE(listener.backend().m_added); - ASSERT_TRUE(static_cast<int>(listener.backend().m_flags) == 0x3); -} - -TEST(ListenerSet, readTwice) -{ - Listener<TestBackendSet> listener; - Handle s = 0; - - listener.set(s, Condition::Readable); - listener.set(s, Condition::Readable); - - ASSERT_EQ(1U, listener.size()); - ASSERT_EQ(1, listener.backend().m_callcount); - ASSERT_TRUE(listener.backend().m_added); - ASSERT_TRUE(listener.backend().m_flags == Condition::Readable); -} - -TEST(ListenerSet, failure) -{ - Listener<TestBackendSetFail> listener; - Handle s = 0; - - try { - listener.set(s, Condition::Readable); - FAIL() << "exception expected"; - } catch (...) { - } - - ASSERT_EQ(0U, listener.size()); -} - -/* -------------------------------------------------------- - * Listener: unset / remove functions - * -------------------------------------------------------- */ - -class TestBackendUnset { -public: - bool m_isset{false}; - bool m_isunset{false}; - Condition m_flags{Condition::None}; - bool m_removal{false}; - - inline void set(const ListenerTable &, Handle &, Condition flags, bool) noexcept - { - m_isset = true; - m_flags |= flags; - } - - inline void unset(const ListenerTable &, Handle &, Condition flags, bool remove) noexcept - { - m_isunset = true; - m_flags &= ~(flags); - m_removal = remove; - } - - std::vector<ListenerStatus> wait(const ListenerTable &, int) noexcept - { - return {}; - } -}; - -class TestBackendUnsetFail { -public: - inline void set(const ListenerTable &, Handle &, Condition, bool) noexcept - { - } - - inline void unset(const ListenerTable &, Handle &, Condition, bool) - { - throw "fail"; - } - - std::vector<ListenerStatus> wait(const ListenerTable &, int) - { - return {}; - } -}; - -TEST(ListenerUnsetRemove, unset) -{ - Listener<TestBackendUnset> listener; - Handle s = 0; - - listener.set(s, Condition::Readable); - listener.unset(s, Condition::Readable); - - ASSERT_EQ(0U, listener.size()); - ASSERT_TRUE(listener.backend().m_isset); - ASSERT_TRUE(listener.backend().m_isunset); - ASSERT_TRUE(listener.backend().m_flags == Condition::None); - ASSERT_TRUE(listener.backend().m_removal); -} - -TEST(ListenerUnsetRemove, unsetOne) -{ - Listener<TestBackendUnset> listener; - Handle s = 0; - - listener.set(s, Condition::Readable | Condition::Writable); - listener.unset(s, Condition::Readable); - - ASSERT_EQ(1U, listener.size()); - ASSERT_TRUE(listener.backend().m_isset); - ASSERT_TRUE(listener.backend().m_isunset); - ASSERT_TRUE(listener.backend().m_flags == Condition::Writable); - ASSERT_FALSE(listener.backend().m_removal); -} - -TEST(ListenerUnsetRemove, unsetAll) -{ - Listener<TestBackendUnset> listener; - Handle s = 0; - - listener.set(s, Condition::Readable | Condition::Writable); - listener.unset(s, Condition::Readable); - listener.unset(s, Condition::Writable); - - ASSERT_EQ(0U, listener.size()); - ASSERT_TRUE(listener.backend().m_isset); - ASSERT_TRUE(listener.backend().m_isunset); - ASSERT_TRUE(listener.backend().m_flags == Condition::None); - ASSERT_TRUE(listener.backend().m_removal); -} - -TEST(ListenerUnsetRemove, remove) -{ - Listener<TestBackendUnset> listener; - Handle s = 0; - - listener.set(s, Condition::Readable | Condition::Writable); - listener.remove(s); - - ASSERT_EQ(0U, listener.size()); - ASSERT_TRUE(listener.backend().m_isset); - ASSERT_TRUE(listener.backend().m_isunset); - ASSERT_TRUE(listener.backend().m_flags == Condition::None); - ASSERT_TRUE(listener.backend().m_removal); -} - -TEST(ListenerUnsetRemove, failure) -{ - Listener<TestBackendUnsetFail> listener; - Handle s = 0; - - listener.set(s, Condition::Readable | Condition::Writable); - - try { - listener.remove(s); - FAIL() << "exception expected"; - } catch (...) { - } - - /* If fail, kept into the table */ - ASSERT_EQ(1U, listener.size()); -} - -/* -------------------------------------------------------- - * Listener: system - * -------------------------------------------------------- */ - -class ListenerTest : public testing::Test { -protected: - Listener<Select> m_listener; - SocketTcp<Ipv4> m_masterTcp; - SocketTcp<Ipv4> m_clientTcp; - - std::thread m_tserver; - std::thread m_tclient; - -public: - ListenerTest() - { - m_masterTcp.set(SockReuseAddress()); - m_masterTcp.bind(Ipv4("*", 16000)); - m_masterTcp.listen(); - } - - ~ListenerTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(ListenerTest, accept) -{ - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp.handle(), Condition::Readable); - m_listener.wait(); - m_masterTcp.accept(nullptr); - m_masterTcp.close(); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_clientTcp.connect(Ipv4("127.0.0.1", 16000)); - }); -} - -TEST_F(ListenerTest, recv) -{ - m_tserver = std::thread([this] () { - try { - m_listener.set(m_masterTcp.handle(), Condition::Readable); - m_listener.wait(); - - SocketTcp<Ipv4> sc; - - m_masterTcp.accept(sc); - - ASSERT_EQ("hello", sc.recv(512)); - } catch (const std::exception &ex) { - FAIL() << ex.what(); - } - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_clientTcp.connect(Ipv4("127.0.0.1", 16000)); - m_clientTcp.send("hello"); - }); -} - -/* -------------------------------------------------------- - * Non-blocking connect - * -------------------------------------------------------- */ - -class NonBlockingConnectTest : public testing::Test { -protected: - SocketTcp<Ipv4> m_server; - SocketTcp<Ipv4> m_client; - - std::thread m_tserver; - std::thread m_tclient; - -public: - NonBlockingConnectTest() - { - m_client.set(SockBlockMode(false)); - } - - ~NonBlockingConnectTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -/* -------------------------------------------------------- - * TCP accept - * -------------------------------------------------------- */ - -class TcpAcceptTest : public testing::Test { -protected: - SocketTcp<Ipv4> m_server; - SocketTcp<Ipv4> m_client; - - std::thread m_tserver; - std::thread m_tclient; - -public: - TcpAcceptTest() - { - m_server.set(SockReuseAddress()); - m_server.bind(Ipv4("*", 16000)); - m_server.listen(); - } - - ~TcpAcceptTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -/* -------------------------------------------------------- - * TCP recv - * -------------------------------------------------------- */ - -class TcpRecvTest : public testing::Test { -protected: - SocketTcp<Ipv4> m_server; - SocketTcp<Ipv4> m_client; - - std::thread m_tserver; - std::thread m_tclient; - -public: - TcpRecvTest() - { - m_server.set(SockReuseAddress()); - m_server.bind(Ipv4("*", 16000)); - m_server.listen(); - } - - ~TcpRecvTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(TcpRecvTest, blockingSuccess) -{ - m_tserver = std::thread([this] () { - SocketTcp<Ipv4> client; - - m_server.accept(client); - - ASSERT_EQ("hello", client.recv(32)); - }); - - std::this_thread::sleep_for(100ms); - - m_tclient = std::thread([this] () { - m_client.connect(Ipv4("127.0.0.1", 16000)); - m_client.send("hello"); - m_client.close(); - }); -} - -/* -------------------------------------------------------- - * Socket SSL - * -------------------------------------------------------- */ - -#if !defined(SOCKET_NO_SSL) - -class TlsRecvTest : public testing::Test { -protected: - SocketTls<Ipv4> m_server{nullptr}; - SocketTls<Ipv4> m_client; - - std::thread m_tserver; - std::thread m_tclient; - -public: - TlsRecvTest() - { - Tls protocol; - - protocol.setCertificate("sockets/test.crt"); - protocol.setPrivateKey("sockets/test.key"); - protocol.setVerify(false); - - m_server = SocketTls<Ipv4>(std::move(protocol)); - m_server.set(SockReuseAddress()); - m_server.bind(Ipv4("*", 16000)); - m_server.listen(); - } - - ~TlsRecvTest() - { - if (m_tserver.joinable()) - m_tserver.join(); - if (m_tclient.joinable()) - m_tclient.join(); - } -}; - -TEST_F(TlsRecvTest, blockingSuccess) -{ - m_tserver = std::thread([this] () { - try { - SocketTls<Ipv4> client; - - m_server.accept(client); - - ASSERT_EQ("hello", client.recv(32)); - } catch (const net::Error &ex) { - FAIL() << ex.function() << ": " << ex.what(); - } - }); - - std::this_thread::sleep_for(250ms); - - m_tclient = std::thread([this] () { - try { - m_client.connect(Ipv4("127.0.0.1", 16000)); - m_client.send("hello"); - } catch (const net::Error &ex) { - FAIL() << ex.function() << ": " << ex.what(); - } - }); -} - -#endif - -int main(int argc, char **argv) -{ - testing::InitGoogleTest(&argc, argv); - - return RUN_ALL_TESTS(); -}
--- a/modules/net/test/test.crt Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICITCCAYoCCQCGm4grkVCohjANBgkqhkiG9w0BAQsFADBVMQswCQYDVQQGEwJG -UjEPMA0GA1UECAwGRnJhbmNlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0 -eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTEwMjYyMDM0NThaFw0yNTEw -MjMyMDM0NThaMFUxCzAJBgNVBAYTAkZSMQ8wDQYDVQQIDAZGcmFuY2UxITAfBgNV -BAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0 -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDp13OqVyOWyv5QWD4xr+Duw6SZ -gU7D5huzsAOcneSI6JUhf+7Ecu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xO -L9fTTK+TwhP2hW/Rf/b2gWedhJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/m -Pdy219f6MAPgODJ/7QIDAQABMA0GCSqGSIb3DQEBCwUAA4GBAJSnn/IBn1ZblfzP -rJO/lE1Jwpmx3B7+oR/e4fkZd6JR3s06umGYWr2H+TPl/5dj9x0gPokhoIL9zCGq -SxCPnOeaxjBkw7yh3Ks6m3xKxmK4aMpAtBHtwmbfQyIcgz71/lfCzbJ3WcKpn1ig -IZbByt5QSSPcFORRzJJa35eHBdfX ------END CERTIFICATE-----
--- a/modules/net/test/test.key Mon Apr 04 17:34:01 2016 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQDp13OqVyOWyv5QWD4xr+Duw6SZgU7D5huzsAOcneSI6JUhf+7E -cu6BQ2JGkFn4srIVkMWGQuImETJ8JCpSQH7rk+xOL9fTTK+TwhP2hW/Rf/b2gWed -hJAS+gilqt4JNT7v2wFv+aTtRt/lpTXVSdtpLa/mPdy219f6MAPgODJ/7QIDAQAB -AoGBANDt4ndQkgi56A1rOm50gVlzTg6lPPXFE/0xB5kYbcdxX0VmI7Q8KCMwTI9V -jD2rk3e3OPSjr6FpfhzyxylkXMBz2BL5NRNPowCJbiMgZOUIzlcWPKo0tgf1bZJx -YdB5U003ISGPPBjVOAjyizY7tJnaNvbpLQ0hbIAsvHPEAOnBAkEA9r3g8NQjPrvb -oIr5SMIxM8HDJ1/q+MEBSFtRFzQpmur6P64Jsu96zCyencUYTxs0L/sottrj6dPC -vjGCc6PjsQJBAPKdqK1knJv6Y95M2bnEwrymCFVdxCi7AxObStB+bg/+7mMCUqqX -j2g71bfvhYakHV7CiaYrrORChwj6vTbimv0CQGpd2IZ5LOhyW2+N+YDgFg3Vzac/ -ti+eJEto8kAqgHUELvUctZmpmypBYe9pc91GQO0ePKL3IaE/ZIhRF4d6c0ECQH9A -XiaD7PiKvjLs0A31u8ZCt4A+7BII9LYl73mntobBSbu4ji9Xyyn6qEAPa1ORZK49 -DwGPSuF2W2lESlYtSOkCQGrtczhx3IyJjk5e2Y1i/UddPKjviAysCSzcW6aVTNr9 -Y2L0sWmva2FKnkl9FDuEqxvmGr6OOkr5Ll7aWLzJri8= ------END RSA PRIVATE KEY-----