changeset 230:4f64b53ecd98

Irccd: update network code
author David Demelier <markand@malikania.fr>
date Wed, 10 Aug 2016 10:33:21 +0200
parents 74d8aa0fd7f5
children 00e2c31469b2
files doc/doxygen/Doxyfile.in irccd/main.cpp irccdctl/main.cpp lib/irccd/CMakeSources.cmake lib/irccd/cmd-help.cpp lib/irccd/cmd-help.hpp lib/irccd/cmd-plugin-config.cpp lib/irccd/cmd-plugin-config.hpp lib/irccd/cmd-plugin-info.cpp lib/irccd/cmd-plugin-info.hpp lib/irccd/cmd-plugin-list.cpp lib/irccd/cmd-plugin-list.hpp lib/irccd/cmd-plugin-load.cpp lib/irccd/cmd-plugin-load.hpp lib/irccd/cmd-plugin-reload.cpp lib/irccd/cmd-plugin-reload.hpp lib/irccd/cmd-plugin-unload.cpp lib/irccd/cmd-plugin-unload.hpp lib/irccd/cmd-server-cmode.cpp lib/irccd/cmd-server-cmode.hpp lib/irccd/cmd-server-cnotice.cpp lib/irccd/cmd-server-cnotice.hpp lib/irccd/cmd-server-connect.cpp lib/irccd/cmd-server-connect.hpp lib/irccd/cmd-server-disconnect.cpp lib/irccd/cmd-server-disconnect.hpp lib/irccd/cmd-server-info.cpp lib/irccd/cmd-server-info.hpp lib/irccd/cmd-server-invite.cpp lib/irccd/cmd-server-invite.hpp lib/irccd/cmd-server-join.cpp lib/irccd/cmd-server-join.hpp lib/irccd/cmd-server-kick.cpp lib/irccd/cmd-server-kick.hpp lib/irccd/cmd-server-list.cpp lib/irccd/cmd-server-list.hpp lib/irccd/cmd-server-me.cpp lib/irccd/cmd-server-me.hpp lib/irccd/cmd-server-message.cpp lib/irccd/cmd-server-message.hpp lib/irccd/cmd-server-mode.cpp lib/irccd/cmd-server-mode.hpp lib/irccd/cmd-server-nick.cpp lib/irccd/cmd-server-nick.hpp lib/irccd/cmd-server-notice.cpp lib/irccd/cmd-server-notice.hpp lib/irccd/cmd-server-part.cpp lib/irccd/cmd-server-part.hpp lib/irccd/cmd-server-reconnect.cpp lib/irccd/cmd-server-reconnect.hpp lib/irccd/cmd-server-topic.cpp lib/irccd/cmd-server-topic.hpp lib/irccd/cmd-watch.cpp lib/irccd/cmd-watch.hpp lib/irccd/command.cpp lib/irccd/command.hpp lib/irccd/config.cpp lib/irccd/conn-state-checking.cpp lib/irccd/conn-state-checking.hpp lib/irccd/conn-state-connecting.cpp lib/irccd/conn-state-connecting.hpp lib/irccd/conn-state-disconnected.cpp lib/irccd/conn-state-disconnected.hpp lib/irccd/conn-state-ready.cpp lib/irccd/conn-state-ready.hpp lib/irccd/conn-state.hpp lib/irccd/connection.cpp lib/irccd/connection.hpp lib/irccd/irccdctl.cpp lib/irccd/irccdctl.hpp lib/irccd/net.hpp lib/irccd/plugin-js.hpp lib/irccd/server-state-connected.hpp lib/irccd/server-state-connecting.hpp lib/irccd/server-state-disconnected.hpp lib/irccd/server-state.hpp lib/irccd/server.hpp lib/irccd/service-command.hpp lib/irccd/service-interrupt.cpp lib/irccd/service-interrupt.hpp lib/irccd/service-server.cpp lib/irccd/service-transport.cpp lib/irccd/service-transport.hpp lib/irccd/service.hpp lib/irccd/transport-client.cpp lib/irccd/transport-client.hpp lib/irccd/transport-server.cpp lib/irccd/transport-server.hpp lib/irccd/util.hpp
diffstat 89 files changed, 3020 insertions(+), 3764 deletions(-) [+]
line wrap: on
line diff
--- a/doc/doxygen/Doxyfile.in	Mon Jul 18 22:39:14 2016 +0200
+++ b/doc/doxygen/Doxyfile.in	Wed Aug 10 10:33:21 2016 +0200
@@ -397,7 +397,7 @@
 # normally produced when WARNINGS is set to YES.
 # The default value is: NO.
 
-EXTRACT_ALL            = NO
+EXTRACT_ALL            = YES
 
 # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
 # be included in the documentation.
@@ -415,7 +415,7 @@
 # included in the documentation.
 # The default value is: NO.
 
-EXTRACT_STATIC         = NO
+EXTRACT_STATIC         = YES
 
 # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
 # locally in source files will be included in the documentation. If set to NO
--- a/irccd/main.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/irccd/main.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -261,7 +261,7 @@
     try {
         load(open(options), options);
     } catch (const std::exception &ex) {
-        log::warning() << "irccd: " << ex.what() << std::endl;
+        log::warning() << "error: " << ex.what() << std::endl;
         return 1;
     }
 
--- a/irccdctl/main.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/irccdctl/main.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -19,6 +19,7 @@
 #include <irccd/irccdctl.hpp>
 #include <irccd/logger.hpp>
 #include <irccd/path.hpp>
+#include <irccd/system.hpp>
 
 using namespace irccd;
 
@@ -29,14 +30,13 @@
     path::setApplicationPath(argv[0]);
     log::setInterface(std::make_unique<log::Console>());
     log::setVerbose(false);
-    net::init();
 
     try {
         Irccdctl ctl;
 
         ctl.run(--argc, ++argv);
     } catch (const std::exception &ex) {
-        log::warning() << sys::programName() << ": " << ex.what() << std::endl;
+        log::warning() << "error: " << ex.what() << std::endl;
         std::exit(1);
     }
 
--- a/lib/irccd/CMakeSources.cmake	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/CMakeSources.cmake	Wed Aug 10 10:33:21 2016 +0200
@@ -2,6 +2,10 @@
     HEADERS
     ${CMAKE_CURRENT_LIST_DIR}/alias.hpp
     ${CMAKE_CURRENT_LIST_DIR}/connection.hpp
+    ${CMAKE_CURRENT_LIST_DIR}/conn-state-checking.hpp
+    ${CMAKE_CURRENT_LIST_DIR}/conn-state-connecting.hpp
+    ${CMAKE_CURRENT_LIST_DIR}/conn-state-disconnected.hpp
+    ${CMAKE_CURRENT_LIST_DIR}/conn-state-ready.hpp
     ${CMAKE_CURRENT_LIST_DIR}/cmd-help.hpp
     ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-config.hpp
     ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-info.hpp
@@ -80,6 +84,10 @@
     SOURCES
     ${CMAKE_CURRENT_LIST_DIR}/alias.cpp
     ${CMAKE_CURRENT_LIST_DIR}/connection.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/conn-state-checking.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/conn-state-connecting.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/conn-state-disconnected.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/conn-state-ready.cpp
     ${CMAKE_CURRENT_LIST_DIR}/config.cpp
     ${CMAKE_CURRENT_LIST_DIR}/cmd-help.cpp
     ${CMAKE_CURRENT_LIST_DIR}/cmd-plugin-config.cpp
--- a/lib/irccd/cmd-help.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-help.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -25,7 +25,7 @@
 namespace command {
 
 Help::Help()
-    : Command("help", "General")
+    : Command("help", "General", "Get help about a command")
 {
 }
 
@@ -34,11 +34,6 @@
     return {{ "command", true }};
 }
 
-std::string Help::help() const
-{
-    return "Get help about a command.";
-}
-
 nlohmann::json Help::request(Irccdctl &irccdctl, const CommandRequest &args) const
 {
     auto it = irccdctl.commandService().find(args.arg(0U));
@@ -46,7 +41,7 @@
     if (!it)
         log::warning() << "there is no command named: " << args.arg(0U) << std::endl;
     else
-        log::warning() << it->usage() << std::flush;
+        log::warning() << it->help() << std::flush;
 
     return nullptr;
 }
--- a/lib/irccd/cmd-help.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-help.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -47,11 +47,6 @@
     IRCCD_EXPORT std::vector<Arg> args() const override;
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::request
      */
     IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
--- a/lib/irccd/cmd-plugin-config.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-config.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -59,15 +59,10 @@
 } // !namespace
 
 PluginConfig::PluginConfig()
-    : Command("plugin-config", "Plugins")
+    : Command("plugin-config", "Plugins", "Get or set a plugin config variable")
 {
 }
 
-std::string PluginConfig::help() const
-{
-    return "Get or set a plugin configuration option.";
-}
-
 std::vector<Command::Arg> PluginConfig::args() const
 {
     return {
--- a/lib/irccd/cmd-plugin-config.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-config.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -41,11 +41,6 @@
     IRCCD_EXPORT PluginConfig();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-plugin-info.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-info.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -29,15 +29,10 @@
 namespace command {
 
 PluginInfo::PluginInfo()
-    : Command("plugin-info", "Plugins")
+    : Command("plugin-info", "Plugins", "Get plugin information")
 {
 }
 
-std::string PluginInfo::help() const
-{
-    return "Get plugin information.";
-}
-
 std::vector<Command::Arg> PluginInfo::args() const
 {
     return {{ "plugin", true }};
--- a/lib/irccd/cmd-plugin-info.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-info.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT PluginInfo();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-plugin-list.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-list.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -29,15 +29,10 @@
 namespace command {
 
 PluginList::PluginList()
-    : Command("plugin-list", "Plugins")
+    : Command("plugin-list", "Plugins", "Get the list of loaded plugins")
 {
 }
 
-std::string PluginList::help() const
-{
-    return "Get the list of loaded plugins.";
-}
-
 nlohmann::json PluginList::exec(Irccd &irccd, const nlohmann::json &request) const
 {
     auto response = Command::exec(irccd, request);
--- a/lib/irccd/cmd-plugin-list.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-list.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT PluginList();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::exec
      */
     IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
--- a/lib/irccd/cmd-plugin-load.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-load.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 PluginLoad::PluginLoad()
-    : Command("plugin-load", "Plugins")
+    : Command("plugin-load", "Plugins", "Load a plugin")
 {
 }
 
-std::string PluginLoad::help() const
-{
-    return "Load a plugin.";
-}
-
 std::vector<Command::Arg> PluginLoad::args() const
 {
     return {{ "plugin", true }};
--- a/lib/irccd/cmd-plugin-load.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-load.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT PluginLoad();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-plugin-reload.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-reload.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -27,15 +27,10 @@
 namespace command {
 
 PluginReload::PluginReload()
-    : Command("plugin-reload", "Plugins")
+    : Command("plugin-reload", "Plugins", "Reload a plugin")
 {
 }
 
-std::string PluginReload::help() const
-{
-    return "Reload a plugin.";
-}
-
 std::vector<Command::Arg> PluginReload::args() const
 {
     return {{ "plugin", true }};
--- a/lib/irccd/cmd-plugin-reload.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-reload.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT PluginReload();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-plugin-unload.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-unload.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 PluginUnload::PluginUnload()
-    : Command("plugin-unload", "Plugins")
+    : Command("plugin-unload", "Plugins", "Unload a plugin")
 {
 }
 
-std::string PluginUnload::help() const
-{
-    return "Unload a plugin.";
-}
-
 std::vector<Command::Arg> PluginUnload::args() const
 {
     return {{ "plugin", true }};
--- a/lib/irccd/cmd-plugin-unload.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-plugin-unload.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT PluginUnload();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-cmode.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-cmode.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerChannelMode::ServerChannelMode()
-    : Command("server-cmode", "Server")
+    : Command("server-cmode", "Server", "Change a channel mode")
 {
 }
 
-std::string ServerChannelMode::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerChannelMode::args() const
 {
     return {
--- a/lib/irccd/cmd-server-cmode.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-cmode.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerChannelMode();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-     
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-cnotice.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-cnotice.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerChannelNotice::ServerChannelNotice()
-    : Command("server-cnotice", "Server")
+    : Command("server-cnotice", "Server", "Send a channel notice")
 {
 }
 
-std::string ServerChannelNotice::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerChannelNotice::args() const
 {
     return {
--- a/lib/irccd/cmd-server-cnotice.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-cnotice.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -51,11 +51,6 @@
     IRCCD_EXPORT ServerChannelNotice();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-connect.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-connect.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -35,15 +35,10 @@
 namespace command {
 
 ServerConnect::ServerConnect()
-    : Command("server-connect", "Server")
+    : Command("server-connect", "Server", "Connect to a server")
 {
 }
 
-std::string ServerConnect::help() const
-{
-    return "Connect to a server.";
-}
-
 std::vector<Command::Option> ServerConnect::options() const
 {
     return {
--- a/lib/irccd/cmd-server-connect.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-connect.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerConnect();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::options
      */
     IRCCD_EXPORT std::vector<Option> options() const override;
--- a/lib/irccd/cmd-server-disconnect.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-disconnect.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerDisconnect::ServerDisconnect()
-    : Command("server-disconnect", "Server")
+    : Command("server-disconnect", "Server", "Disconnect one or more servers")
 {
 }
 
-std::string ServerDisconnect::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerDisconnect::args() const
 {
     return {{ "server", false }};
--- a/lib/irccd/cmd-server-disconnect.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-disconnect.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerDisconnect();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * Get list of arguments required.
      *
      * \return the arguments required
--- a/lib/irccd/cmd-server-info.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-info.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -28,15 +28,10 @@
 namespace command {
 
 ServerInfo::ServerInfo()
-    : Command("server-info", "Server")
+    : Command("server-info", "Server", "Get server information")
 {
 }
 
-std::string ServerInfo::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerInfo::args() const
 {
     return {{ "server", true }};
--- a/lib/irccd/cmd-server-info.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-info.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerInfo();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-invite.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-invite.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerInvite::ServerInvite()
-    : Command("server-invite", "Server")
+    : Command("server-invite", "Server", "Invite someone into a channel")
 {
 }
 
-std::string ServerInvite::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerInvite::args() const
 {
     return {
--- a/lib/irccd/cmd-server-invite.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-invite.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerInvite();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-join.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-join.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerJoin::ServerJoin()
-    : Command("server-join", "Server")
+    : Command("server-join", "Server", "Join a channel")
 {
 }
 
-std::string ServerJoin::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerJoin::args() const
 {
     return {
--- a/lib/irccd/cmd-server-join.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-join.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerJoin();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-kick.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-kick.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerKick::ServerKick()
-    : Command("server-kick", "Server")
+    : Command("server-kick", "Server", "Kick someone from a channel")
 {
 }
 
-std::string ServerKick::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerKick::args() const
 {
     return {
--- a/lib/irccd/cmd-server-kick.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-kick.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerKick();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-list.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-list.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -28,15 +28,10 @@
 namespace command {
 
 ServerList::ServerList()
-    : Command("server-list", "Server")
+    : Command("server-list", "Server", "Get the list of servers")
 {
 }
 
-std::string ServerList::help() const
-{
-    return "";
-}
-
 nlohmann::json ServerList::exec(Irccd &irccd, const nlohmann::json &) const
 {
     auto json = nlohmann::json::object();
@@ -52,9 +47,14 @@
 
 void ServerList::result(Irccdctl &, const nlohmann::json &response) const
 {
-    if (response.count("list") != 0)
-        for (const auto &n : response["list"])
-            std::cout << n.dump() << std::endl;
+    auto list = response.find("list");
+
+    if (list == response.end())
+        return;
+
+    for (auto v : *list)
+        if (v.is_string())
+            std::cout << v.get<std::string>() << std::endl;
 }
 
 } // !command
--- a/lib/irccd/cmd-server-list.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-list.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerList();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::exec
      */
     IRCCD_EXPORT nlohmann::json exec(Irccd &irccd, const nlohmann::json &request) const override;
--- a/lib/irccd/cmd-server-me.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-me.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerMe::ServerMe()
-    : Command("server-me", "Server")
+    : Command("server-me", "Server", "Send an action emote")
 {
 }
 
-std::string ServerMe::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerMe::args() const
 {
     return {
--- a/lib/irccd/cmd-server-me.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-me.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerMe();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-message.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-message.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerMessage::ServerMessage()
-    : Command("server-message", "Server")
+    : Command("server-message", "Server", "Send a message")
 {
 }
 
-std::string ServerMessage::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerMessage::args() const
 {
     return {
--- a/lib/irccd/cmd-server-message.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-message.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerMessage();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-mode.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-mode.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerMode::ServerMode()
-    : Command("server-mode", "Server")
+    : Command("server-mode", "Server", "Change your mode")
 {
 }
 
-std::string ServerMode::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerMode::args() const
 {
     return {
--- a/lib/irccd/cmd-server-mode.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-mode.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerMode();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-nick.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-nick.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerNick::ServerNick()
-    : Command("server-nick", "Server")
+    : Command("server-nick", "Server", "Change your nickname")
 {
 }
 
-std::string ServerNick::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerNick::args() const
 {
     return {
--- a/lib/irccd/cmd-server-nick.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-nick.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerNick();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-notice.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-notice.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerNotice::ServerNotice()
-    : Command("server-notice", "Server")
+    : Command("server-notice", "Server", "Send a private notice")
 {
 }
 
-std::string ServerNotice::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerNotice::args() const
 {
     return {
--- a/lib/irccd/cmd-server-notice.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-notice.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerNotice();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-part.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-part.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerPart::ServerPart()
-    : Command("server-part", "Server")
+    : Command("server-part", "Server", "Leave a channel")
 {
 }
 
-std::string ServerPart::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerPart::args() const
 {
     return {
--- a/lib/irccd/cmd-server-part.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-part.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerPart();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-reconnect.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-reconnect.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerReconnect::ServerReconnect()
-    : Command("server-reconnect", "Server")
+    : Command("server-reconnect", "Server", "Force reconnection of one or more servers")
 {
 }
 
-std::string ServerReconnect::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerReconnect::args() const
 {
     return {{ "server", false }};
--- a/lib/irccd/cmd-server-reconnect.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-reconnect.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerReconnect();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-server-topic.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-topic.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -26,15 +26,10 @@
 namespace command {
 
 ServerTopic::ServerTopic()
-    : Command("server-topic", "Server")
+    : Command("server-topic", "Server", "Change a channel topic")
 {
 }
 
-std::string ServerTopic::help() const
-{
-    return "";
-}
-
 std::vector<Command::Arg> ServerTopic::args() const
 {
     return {
--- a/lib/irccd/cmd-server-topic.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-server-topic.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -42,11 +42,6 @@
     IRCCD_EXPORT ServerTopic();
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::args
      */
     IRCCD_EXPORT std::vector<Arg> args() const override;
--- a/lib/irccd/cmd-watch.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-watch.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -198,7 +198,8 @@
 } // !namespace
 
 Watch::Watch()
-    : Command("watch", "General")
+    : Command(
+        "watch", "General", "Start watching irccd events")
 {
 }
 
@@ -207,17 +208,6 @@
     return {{ "format", "f", "format", "format", "output format" }};
 }
 
-std::string Watch::help() const
-{
-    std::ostringstream oss;
-
-    oss << "Start watching irccd events.\n\n";
-    oss << "You can use different output formats, native which is a human readable\n";
-    oss << "format or json, pretty formatted json.";
-
-    return oss.str();
-}
-
 nlohmann::json Watch::request(Irccdctl &ctl, const CommandRequest &request) const
 {
     std::string format = request.optionOr("format", "native");
@@ -225,9 +215,9 @@
     if (format != "native" && format != "json")
         throw std::invalid_argument("invalid format given: " + format);
 
-    for (;;) {
+    while (ctl.connection().isConnected()) {
         try {
-            auto object = ctl.connection().next(-1);
+            auto object = ctl.next();
             auto event = object.find("event");
 
             if (event == object.end() || !event->is_string())
@@ -249,8 +239,6 @@
         }
     }
 
-    throw std::runtime_error("connection lost");
-
     return nullptr;
 }
 
--- a/lib/irccd/cmd-watch.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/cmd-watch.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -47,11 +47,6 @@
     IRCCD_EXPORT std::vector<Option> options() const override;
 
     /**
-     * \copydoc Command::help
-     */
-    IRCCD_EXPORT std::string help() const override;
-
-    /**
      * \copydoc Command::request
      */
     IRCCD_EXPORT nlohmann::json request(Irccdctl &irccdctl, const CommandRequest &args) const override;
--- a/lib/irccd/command.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/command.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -143,6 +143,45 @@
 {
     std::ostringstream oss;
 
+    oss << m_name << " ";
+
+    // Options.
+    auto optlist = options();
+
+    if (optlist.size() > 0) {
+        for (const auto &opt : optlist) {
+            oss << "[";
+
+            /*
+             * Long options are too big so only show them in the help
+             * command usage or only if no short option is available.
+             */
+            if (opt.simpleKey().size() > 0)
+                oss << "-" << opt.simpleKey();
+            else if (opt.longKey().size() > 0)
+                oss << " --" << opt.longKey();
+
+            oss << (opt.arg().empty() ? "" : " ") << opt.arg() << "] ";
+        }
+    }
+
+    // Arguments.
+    auto argslist = args();
+
+    if (argslist.size() > 0) {
+        for (const auto &arg : argslist)
+            oss << (arg.required() ? "" : "[")
+                << arg.name()
+                << (arg.required() ? "" : "]") << " ";
+    }
+
+    return oss.str();
+}
+
+std::string Command::help() const
+{
+    std::ostringstream oss;
+
     oss << "usage: " << sys::programName() << " " << m_name;
 
     // Options summary.
@@ -158,7 +197,7 @@
     }
 
     // Description.
-    oss << "\n\n" << help() << "\n\n";
+    oss << "\n\n" << m_description << "\n\n";
 
     // Options.
     if (options().size() > 0) {
--- a/lib/irccd/command.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/command.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -336,6 +336,7 @@
 private:
     std::string m_name;
     std::string m_category;
+    std::string m_description;
     bool m_visible;
 
 public:
@@ -346,11 +347,16 @@
      * \pre category must not be empty
      * \param name the command name (e.g. server-list)
      * \param category the category (e.g. Server)
+     * \param description a one line description with no dots, no new line
      * \param visible true if the command should be visible without verbosity
      */
-    inline Command(std::string name, std::string category, bool visible = true) noexcept
+    inline Command(std::string name,
+                   std::string category,
+                   std::string description,
+                   bool visible = true) noexcept
         : m_name(std::move(name))
         , m_category(std::move(category))
+        , m_description(std::move(description))
         , m_visible(visible)
     {
         assert(!m_name.empty());
@@ -385,6 +391,16 @@
     }
 
     /**
+     * Get the command description.
+     *
+     * \return the description
+     */
+    inline const std::string &description() const noexcept
+    {
+        return m_description;
+    }
+
+    /**
      * Hide the command in non-verbose mode.
      *
      * \return true if the command should be visible in non-verbose mode
@@ -406,7 +422,7 @@
      *
      * \return the help message
      */
-    virtual std::string help() const = 0;
+    IRCCD_EXPORT std::string help() const;
 
     /**
      * Get the supported irccdctl options.
@@ -489,7 +505,7 @@
      * What to do when receiving the response from irccd.
      *
      * This default implementation just check for an error string and shows it if any.
-     * 
+     *
      * \param irccdctl the irccdctl instance
      * \param response the JSON response
      */
--- a/lib/irccd/config.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/config.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -174,9 +174,9 @@
     }
 
     if (ipv6)
-        transport = std::make_shared<TransportServerIp>(AF_INET6, move(address), port, !ipv4);
+        transport = std::make_shared<TransportServerIpv6>(move(address), port, !ipv4);
     else if (ipv4)
-        transport = std::make_shared<TransportServerIp>(AF_INET, move(address), port);
+        transport = std::make_shared<TransportServerIp>(move(address), port);
     else
         throw std::invalid_argument("transport: domain must at least have ipv4 or ipv6");
 
@@ -193,7 +193,7 @@
     if (it == sc.end())
         throw std::invalid_argument("transport: missing 'path' parameter");
 
-    return std::make_shared<TransportServerUnix>(it->value());
+    return std::make_shared<TransportServerLocal>(it->value());
 #else
     (void)sc;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/conn-state-checking.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -0,0 +1,101 @@
+/*
+ * conn-state-checking.cpp -- verify irccd instance
+ *
+ * Copyright (c) 2013-2016 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 <format.h>
+
+#include "conn-state-checking.hpp"
+#include "conn-state-disconnected.hpp"
+#include "conn-state-ready.hpp"
+#include "sysconfig.hpp"
+
+using namespace fmt::literals;
+
+namespace irccd {
+
+void Connection::CheckingState::verifyProgram(const nlohmann::json &json) const
+{
+    auto prog = json.find("program");
+
+    if (prog == json.end() || !prog->is_string() || prog->get<std::string>() != "irccd")
+        throw std::runtime_error("not an irccd instance");
+}
+
+void Connection::CheckingState::verifyVersion(Connection &cnx, const nlohmann::json &json) const
+{
+    auto getVersionVar = [&] (auto key) {
+        auto it = json.find(key);
+
+        if (it == json.end() || !it->is_number_unsigned())
+            throw std::runtime_error("invalid irccd instance");
+
+        return *it;
+    };
+
+    Info info{
+        getVersionVar("major"),
+        getVersionVar("minor"),
+        getVersionVar("patch")
+    };
+
+    // Ensure compatibility.
+    if (info.major != IRCCD_VERSION_MAJOR || info.minor > IRCCD_VERSION_MINOR)
+        throw std::runtime_error("server version too recent {}.{}.{} vs {}.{}.{}"_format(
+            info.major, info.minor, info.patch,
+            IRCCD_VERSION_MAJOR, IRCCD_VERSION_MINOR, IRCCD_VERSION_PATCH));
+
+    // Successfully connected.
+    cnx.m_stateNext = std::make_unique<ReadyState>();
+    cnx.onConnect(info);
+}
+
+void Connection::CheckingState::verify(Connection &cnx) const
+{
+    auto msg = util::nextNetwork(cnx.m_input);
+
+    if (msg.empty())
+        return;
+
+    try {
+        auto json = nlohmann::json::parse(msg);
+
+        verifyProgram(json);
+        verifyVersion(cnx, json);
+    } catch (const std::exception &ex) {
+        cnx.m_stateNext = std::make_unique<DisconnectedState>();
+        cnx.onDisconnect(ex.what());
+    }
+}
+
+Connection::Status Connection::CheckingState::status() const noexcept
+{
+    return Checking;
+}
+
+void Connection::CheckingState::prepare(Connection &cnx, fd_set &in, fd_set &)
+{
+    FD_SET(cnx.m_socket.handle(), &in);
+}
+
+void Connection::CheckingState::sync(Connection &cnx, fd_set &, fd_set &)
+{
+    cnx.syncInput();
+
+    verify(cnx);
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/conn-state-checking.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -0,0 +1,65 @@
+/*
+ * conn-state-checking.hpp -- verify irccd instance
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_CONN_STATE_CHECKING_HPP
+#define IRCCD_CONN_STATE_CHECKING_HPP
+
+/**
+ * \file conn-state-checking.hpp
+ * \brief Verify irccd instance and version
+ */
+
+#include "conn-state.hpp"
+
+namespace irccd {
+
+/**
+ * \brief State for veryfing connection.
+ *
+ * This state is used when socket connection is complete but we have not
+ * verified that the endpoint is an irccd instance.
+ *
+ * This state also verifies that the irccd daemon is compatible with
+ * our library.
+ */
+class Connection::CheckingState : public Connection::State {
+private:
+    void verifyProgram(const nlohmann::json &json) const;
+    void verifyVersion(Connection &cnx, const nlohmann::json &json) const;
+    void verify(Connection &cnx) const;
+
+public:
+    /**
+     * \copydoc State::status
+     */
+    Status status() const noexcept override;
+
+    /**
+     * \copydoc State::prepare
+     */
+    void prepare(Connection &cnt, fd_set &in, fd_set &out) override;
+
+    /**
+     * \copydoc State::sync
+     */
+    void sync(Connection &cnt, fd_set &in, fd_set &out) override;
+};
+
+} // !irccd
+
+#endif // !IRCCD_CONN_STATE_CHECKING_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/conn-state-connecting.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -0,0 +1,54 @@
+/*
+ * conn-state-connecting.cpp -- connection is in progress
+ *
+ * Copyright (c) 2013-2016 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 "conn-state-checking.hpp"
+#include "conn-state-connecting.hpp"
+#include "conn-state-disconnected.hpp"
+
+namespace irccd {
+
+Connection::Status Connection::ConnectingState::status() const noexcept
+{
+    return Connecting;
+}
+
+void Connection::ConnectingState::prepare(Connection &cnx, fd_set &, fd_set &out)
+{
+    FD_SET(cnx.m_socket.handle(), &out);
+}
+
+void Connection::ConnectingState::sync(Connection &cnx, fd_set &, fd_set &out)
+{
+    if (!FD_ISSET(cnx.m_socket.handle(), &out))
+        return;
+
+    try {
+        auto errc = cnx.m_socket.get<int>(SOL_SOCKET, SO_ERROR);
+
+        if (errc != 0) {
+            cnx.m_stateNext = std::make_unique<DisconnectedState>();
+            cnx.onDisconnect(net::error(errc));
+        } else
+            cnx.m_stateNext = std::make_unique<CheckingState>();
+    } catch (const std::exception &ex) {
+        cnx.m_stateNext = std::make_unique<DisconnectedState>();
+        cnx.onDisconnect(ex.what());
+    }
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/conn-state-connecting.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -0,0 +1,57 @@
+/*
+ * conn-state-connecting.hpp -- connection is in progress
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_CONN_STATE_CONNECTING_HPP
+#define IRCCD_CONN_STATE_CONNECTING_HPP
+
+/**
+ * \file conn-state-connecting.hpp
+ * \brief Connection is in progress.
+ */
+
+#include "conn-state.hpp"
+
+namespace irccd {
+
+/**
+ * \brief State to complete socket connection.
+ *
+ * This state is used to complete the socket connection if it has not been
+ * completed immediately.
+ */
+class Connection::ConnectingState : public Connection::State {
+public:
+    /**
+     * \copydoc State::status
+     */
+    Status status() const noexcept override;
+
+    /**
+     * \copydoc State::prepare
+     */
+    void prepare(Connection &cnt, fd_set &in, fd_set &out) override;
+
+    /**
+     * \copydoc State::sync
+     */
+    void sync(Connection &cnt, fd_set &in, fd_set &out) override;
+};
+
+} // !irccd
+
+#endif // !IRCCD_CONN_STATE_CONNECTING_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/conn-state-disconnected.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -0,0 +1,36 @@
+/*
+ * conn-state-disconnected.cpp -- disconnected state
+ *
+ * Copyright (c) 2013-2016 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 "conn-state-disconnected.hpp"
+
+namespace irccd {
+
+Connection::Status Connection::DisconnectedState::status() const noexcept
+{
+    return Disconnected;
+}
+
+void Connection::DisconnectedState::prepare(Connection &, fd_set &, fd_set &)
+{
+}
+
+void Connection::DisconnectedState::sync(Connection &, fd_set &, fd_set &)
+{
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/conn-state-disconnected.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -0,0 +1,57 @@
+/*
+ * conn-state-disconnected.hpp -- disconnected state
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_CONN_STATE_DISCONNECTED_HPP
+#define IRCCD_CONN_STATE_DISCONNECTED_HPP
+
+/**
+ * \file conn-state-disconnected.hpp
+ * \brief Disconnected.
+ */
+#include "conn-state.hpp"
+
+namespace irccd {
+
+/**
+ * \brief Disconnected state.
+ *
+ * No-op state.
+ *
+ * This state does nothing, it is the default one and the last one.
+ */
+class Connection::DisconnectedState : public Connection::State {
+public:
+    /**
+     * \copydoc State::status
+     */
+    Status status() const noexcept override;
+
+    /**
+     * \copydoc State::prepare
+     */
+    void prepare(Connection &cnt, fd_set &in, fd_set &out) override;
+
+    /**
+     * \copydoc State::sync
+     */
+    void sync(Connection &cnt, fd_set &in, fd_set &out) override;
+};
+
+} // !irccd
+
+#endif // !IRCCD_CONN_STATE_DISCONNECTED_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/conn-state-ready.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -0,0 +1,72 @@
+/*
+ * conn-state-ready.cpp -- connection is ready for I/O
+ *
+ * Copyright (c) 2013-2016 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 "conn-state-ready.hpp"
+
+namespace irccd {
+
+namespace {
+
+void parse(Connection &cnx, const std::string &message)
+{
+    try {
+        auto json = nlohmann::json::parse(message);
+
+        if (!json.is_object())
+            return;
+
+        cnx.onMessage(json);
+    } catch (const std::exception &) {
+    }
+}
+
+} // !namespace
+
+Connection::Status Connection::ReadyState::status() const noexcept
+{
+    return Ready;
+}
+
+void Connection::ReadyState::prepare(Connection &cnx, fd_set &in, fd_set &out)
+{
+    FD_SET(cnx.m_socket.handle(), &in);
+
+    if (!cnx.m_output.empty())
+        FD_SET(cnx.m_socket.handle(), &out);
+}
+
+void Connection::ReadyState::sync(Connection &cnx, fd_set &in, fd_set &out)
+{
+    if (FD_ISSET(cnx.m_socket.handle(), &out))
+        cnx.syncOutput();
+
+    if (FD_ISSET(cnx.m_socket.handle(), &in)) {
+        cnx.syncInput();
+
+        std::string msg;
+
+        do {
+            msg = util::nextNetwork(cnx.m_input);
+
+            if (!msg.empty())
+                parse(cnx, msg);
+        } while (!msg.empty());
+    }
+}
+
+} // !irccd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/conn-state-ready.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -0,0 +1,60 @@
+/*
+ * conn-state-ready.hpp -- connection is ready for I/O
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_CONN_STATE_READY_HPP
+#define IRCCD_CONN_STATE_READY_HPP
+
+/**
+ * \file conn-state-ready.hpp
+ * \brief Connection is ready.
+ */
+
+#include "conn-state.hpp"
+
+namespace irccd {
+
+/**
+ * \brief Ready state.
+ *
+ * This state is used when the connection to irccd is complete, including
+ * irccd daemon verification and optional handshaking.
+ *
+ * It's the only state that may trigger onEvent and onResponse signals
+ * from the Connection.
+ */
+class Connection::ReadyState : public Connection::State {
+public:
+    /**
+     * \copydoc State::status
+     */
+    Status status() const noexcept override;
+
+    /**
+     * \copydoc State::prepare
+     */
+    void prepare(Connection &cnt, fd_set &in, fd_set &out) override;
+
+    /**
+     * \copydoc State::sync
+     */
+    void sync(Connection &cnt, fd_set &in, fd_set &out) override;
+};
+
+} // !irccd
+
+#endif // !IRCCD_CONN_STATE_READY_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/irccd/conn-state.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -0,0 +1,75 @@
+/*
+ * conn-state.hpp -- abstract state for Connection object
+ *
+ * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_CONN_STATE_HPP
+#define IRCCD_CONN_STATE_HPP
+
+/**
+ * \file conn-state.hpp
+ * \brief State for Connection.
+ */
+
+#include "connection.hpp"
+
+namespace irccd {
+
+/**
+ * \brief Abstract state interface for Connection
+ *
+ * Abstract state interface for Connection.
+ *
+ * The Connection is event based, you should not throw exceptions from the
+ * prepare or sync functions, instead you should change the Connection state
+ * and emit the onDisconnect signal.
+ */
+class Connection::State {
+public:
+    /**
+     * Return the state.
+     *
+     * \return the state
+     */
+    virtual Status status() const noexcept = 0;
+
+    /**
+     * Prepare the input and output sets.
+     *
+     * You should not change the connection state in this function.
+     *
+     * \param cnt the connection object
+     * \param in the input set
+     * \param out the output set
+     */
+    virtual void prepare(Connection &cnt, fd_set &in, fd_set &out) = 0;
+
+    /**
+     * Synchronize network I/O in the implementation.
+     *
+     * You should change the connection state using cnx.m_nextState
+     * if needed.
+     *
+     * \param cnt the connection object
+     * \param in the input set
+     * \param out the output set
+     */
+    virtual void sync(Connection &cnt, fd_set &in, fd_set &out) = 0;
+};
+
+} // !irccd
+
+#endif // !IRCCD_CONN_STATE_HPP
--- a/lib/irccd/connection.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/connection.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -16,34 +16,102 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <stdexcept>
+
 #include "connection.hpp"
-#include "logger.hpp"
+#include "conn-state-connecting.hpp"
+#include "conn-state-checking.hpp"
+#include "conn-state-disconnected.hpp"
 #include "util.hpp"
 
 namespace irccd {
 
-nlohmann::json Connection::next(const std::string &name, int timeout)
+void Connection::syncInput()
 {
-    m_timer.reset();
+    try {
+        std::string buffer;
+
+        buffer.resize(512);
+        buffer.resize(m_socket.recv(&buffer[0], buffer.size()));
+
+        if (buffer.empty())
+            throw std::runtime_error("connection lost");
+
+        m_input += std::move(buffer);
+    } catch (const std::exception &ex) {
+        m_stateNext = std::make_unique<DisconnectedState>();
+        onDisconnect(ex.what());
+    }
+}
 
-    for (;;) {
-        auto object = next(clamp(timeout));
-        auto response = object.find("response");
+void Connection::syncOutput()
+{
+    try {
+        auto ns = m_socket.send(m_output.data(), m_output.length());
 
-        if (response != object.end() && util::json::toString(*response) == name)
-            return object;
+        if (ns > 0)
+            m_output.erase(0, ns);
+    } catch (const std::exception &ex) {
+        m_stateNext = std::make_unique<DisconnectedState>();
+        onDisconnect(ex.what());
     }
+}
 
-    throw std::runtime_error("connection lost");
+Connection::Connection()
+    : m_state(std::make_unique<DisconnectedState>())
+{
+}
+
+Connection::~Connection() = default;
+
+Connection::Status Connection::status() const noexcept
+{
+    return m_state->status();
 }
 
-void Connection::verify(const std::string &name, int timeout)
+void Connection::connect(const net::Address &address)
+{
+    assert(status() == Disconnected);
+
+    try {
+        m_socket = net::TcpSocket(address.domain(), 0);
+        m_socket.set(net::option::SockBlockMode(false));
+        m_socket.connect(address);
+        m_state = std::make_unique<CheckingState>();
+    } catch (const net::WouldBlockError &) {
+        m_state = std::make_unique<ConnectingState>();
+    } catch (const std::exception &ex) {
+        m_state = std::make_unique<DisconnectedState>();
+        onDisconnect(ex.what());
+    }
+}
+
+void Connection::prepare(fd_set &in, fd_set &out, net::Handle &max)
 {
-    auto object = next(name, timeout);
-    auto value = object.at("status").dump();
+    try {
+        m_state->prepare(*this, in, out);
+
+        if (m_socket.handle() > max)
+            max = m_socket.handle();
+    } catch (const std::exception &ex) {
+        m_state = std::make_unique<DisconnectedState>();
+        onDisconnect(ex.what());
+    }
+}
 
-    if (!value.empty() && value != "ok")
-        throw std::runtime_error(object.at("error").dump());
+void Connection::sync(fd_set &in, fd_set &out)
+{
+    try {
+        m_state->sync(*this, in, out);
+
+        if (m_stateNext) {
+            m_state = std::move(m_stateNext);
+            m_stateNext = nullptr;
+        }
+    } catch (const std::exception &ex) {
+        m_state = std::make_unique<DisconnectedState>();
+        onDisconnect(ex.what());
+    }
 }
 
 } // !irccd
--- a/lib/irccd/connection.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/connection.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -25,211 +25,192 @@
  */
 
 #include <cassert>
-#include <stdexcept>
+#include <memory>
+#include <string>
 
-#include "elapsed-timer.hpp"
-#include "json.hpp"
 #include "net.hpp"
-#include "sysconfig.hpp"
-#include "system.hpp"
-#include "util.hpp"
+#include "signals.hpp"
+#include "service.hpp"
 
 namespace irccd {
 
 /**
- * \class Connection
- * \brief Abstract class for connecting to irccd from Ip or Local addresses.
+ * \brief Low level connection to irccd instance.
+ *
+ * This class is an event-based connection to an irccd instance. You can use
+ * it directly if you want to issue commands to irccd in an asynchronous way.
+ *
+ * Being asynchronous makes mixing the event loop with this connection easier.
+ *
+ * It is implemented as a finite state machine as it may requires several
+ * roundtrips between the controller and irccd.
+ *
+ * Be aware that there are no namespaces for commands, if you plan to use
+ * Irccdctl class and you also connect the onMessage signal, irccdctl will also
+ * use it. Do not use irccdctl directly if this is a concern.
+ *
+ * The state may change as following.
+ *
+ *   [o]
+ *    |       +----------------------------+
+ *    v       v                            |
+ * +--------------+   +----------+     +----------------+
+ * | Disconnected |-->| Checking |---->| Authenticating |
+ * +--------------+   +----------+     +----------------+
+ *     ^       |            ^              |
+ *     |       |            |              v
+ *     |       |      +------------+   +-------+
+ *     |       +----->| Connecting |<--| Ready |
+ *     |              +------------+   +-------+
+ *     |                                   |
+ *     ------------------------------------+
+ *
+ * Note: authenticating state is not implemented yet.
  */
-class Connection {
-protected:
+class Connection : public Service {
+public:
     /**
-     * Timer to track elapsed time.
+     * \brief The current connection state.
      */
-    ElapsedTimer m_timer;
+    enum Status {
+        Disconnected,       //!< Socket is closed
+        Connecting,         //!< Connection is in progress
+        Checking,           //!< Connection is checking irccd daemon
+        Authenticating,     //!< Connection is authenticating
+        Ready               //!< Socket is ready for I/O
+    };
 
     /**
-     * Clamp the time to wait to be sure that it will be never less than 0.
+     * \brief Irccd information.
+     */
+    class Info {
+    public:
+        unsigned short major;
+        unsigned short minor;
+        unsigned short patch;
+    };
+
+    /**
+     * onConnect
+     * --------------------------------------------------------------
+     *
+     * Connection was successfull.
+     */
+    Signal<const Info &> onConnect;
+
+    /**
+     * onMessage
+     * ---------------------------------------------------------------
+     *
+     * Upon message.
      */
-    inline int clamp(int timeout) noexcept
-    {
-        return timeout < 0 ? -1 : (timeout - (int)m_timer.elapsed() < 0) ? 0 : (timeout - m_timer.elapsed());
-    }
+    Signal<const nlohmann::json &> onMessage;
+
+    /**
+     * onDisconnect
+     * --------------------------------------------------------------
+     *
+     * A fatal error occured resulting in disconnection.
+     */
+    Signal<const std::string &> onDisconnect;
+
+private:
+    std::string m_input;
+    std::string m_output;
+
+public:
+    /**
+     * \brief TEST
+     */
+    class State;
+    class DisconnectedState;
+    class ConnectingState;
+    /**
+     * \brief TEST
+     */
+    class CheckingState;
+    class ReadyState;
+
+private:
+    std::unique_ptr<State> m_state;
+    std::unique_ptr<State> m_stateNext;
+
+protected:
+    net::TcpSocket m_socket{net::Invalid};
+
+    void syncInput();
+    void syncOutput();
 
 public:
     /**
      * Default constructor.
      */
-    Connection() = default;
+    Connection();
 
     /**
      * Default destructor.
      */
-    virtual ~Connection() = default;
-
-    /**
-     * Wait for the next requested response.
-     *
-     * \param name the response name
-     * \param timeout the optional timeout
-     * \return the object
-     * \throw net::Error on errors or on timeout
-     */
-    IRCCD_EXPORT nlohmann::json next(const std::string &name, int timeout = 30000);
-
-    /**
-     * Just wait if the operation succeeded.
-     *
-     * \param name the response name
-     * \param timeout the timeout
-     */
-    IRCCD_EXPORT void verify(const std::string &name, int timeout = 30000);
-
-    /**
-     * Try to connect to the host.
-     *
-     * \param timeout the maximum time in milliseconds
-     * \throw net::Error on errors or timeout
-     */
-    virtual void connect(int timeout = 30000) = 0;
+    virtual ~Connection();
 
     /**
-     * Try to send the message in 30 seconds. The message must not end with \\r\\n\\r\\n, it is added automatically.
+     * Send an asynchronous request to irccd.
      *
-     * \pre msg must not be empty
-     * \param msg the message to send
-     * \param timeout the maximum time in milliseconds
-     * \throw net::Error on errors
+     * \pre json.is_object
+     * \param json the JSON object
      */
-    virtual void send(std::string msg, int timeout = 30000) = 0;
+    inline void request(const nlohmann::json &json)
+    {
+        assert(json.is_object());
+
+        m_output += json.dump();
+        m_output += "\r\n\r\n";
+    }
 
     /**
-     * Get the next event from irccd.
-     *
-     * This functions throws if the connection is lost.
+     * Get the underlying socket handle.
      *
-     * \param timeout the maximum time in milliseconds
-     * \return the next event
-     * \throw net::Error on errors or disconnection
+     * \return the handle
      */
-    virtual nlohmann::json next(int timeout = 30000) = 0;
-};
-
-/**
- * \class ConnectionBase
- * \brief Implementation for Ip or Local.
- */
-template <typename Address>
-class ConnectionBase : public Connection {
-private:
-    net::SocketTcp<Address> m_socket;
-    net::Listener<> m_listener;
-    Address m_address;
-
-    // Input buffer.
-    std::string m_input;
-
-public:
-    /**
-     * Construct the socket but do not connect immediately.
-     *
-     * \param address the address
-     */
-    inline ConnectionBase(Address address)
-        : m_address(std::move(address))
+    inline net::Handle handle() const noexcept
     {
-        m_socket.set(net::option::SockBlockMode{false});
-        m_listener.set(m_socket.handle(), net::Condition::Readable);
+        return m_socket.handle();
     }
 
     /**
-     * \copydoc Connection::connect
-     */
-    void connect(int timeout) override;
-
-    /**
-     * \copydoc Connection::send
-     */
-    void send(std::string msg, int timeout) override;
-
-    /**
-     * \copydoc Connection::next(int)
+     * Shorthand for state() != Disconnected.
+     *
+     * \return true if state() != Disconnected
      */
-    nlohmann::json next(int timeout) override;
-};
-
-template <typename Address>
-void ConnectionBase<Address>::connect(int timeout)
-{
-    net::Condition cond;
-
-    m_socket.connect(m_address, cond);
-    m_timer.reset();
-
-    while (cond != net::Condition::None) {
-        if (timeout >= 0 && m_timer.elapsed() >= static_cast<unsigned>(timeout))
-            throw std::runtime_error("timeout while connecting");
-
-        m_listener.remove(m_socket.handle());
-        m_listener.set(m_socket.handle(), cond);
-        m_listener.wait(timeout - m_timer.elapsed());
-        m_socket.resumeConnect(cond);
+    inline bool isConnected() const noexcept
+    {
+        return status() != Disconnected;
     }
 
-    m_listener.set(m_socket.handle(), net::Condition::Readable);
-}
-
-template <typename Address>
-void ConnectionBase<Address>::send(std::string msg, int timeout)
-{
-    assert(!msg.empty());
-
-    // Add termination.
-    msg += "\r\n\r\n";
-
-    m_listener.remove(m_socket.handle());
-    m_listener.set(m_socket.handle(), net::Condition::Writable);
-    m_timer.reset();
-
-    while (!msg.empty()) {
-        // Do not wait the time that is already passed.
-        m_listener.wait(clamp(timeout));
-
-        // Try to send at most as possible.
-        msg.erase(0, m_socket.send(msg));
-    }
-
-    // Timeout?
-    if (!msg.empty())
-        throw std::runtime_error("operation timed out while sending to irccd");
-}
+    /**
+     * Get the current state.
+     *
+     * \return the state
+     */
+    Status status() const noexcept;
 
-template <typename Address>
-nlohmann::json ConnectionBase<Address>::next(int timeout)
-{
-    // Maybe there is already something.
-    std::string buffer = util::nextNetwork(m_input);
-
-    m_listener.remove(m_socket.handle());
-    m_listener.set(m_socket.handle(), net::Condition::Readable);
-    m_timer.reset();
+    /**
+     * Initiate connection to irccd.
+     *
+     * \pre state() == Disconnected
+     * \param address the address
+     */
+    virtual void connect(const net::Address &address);
 
-    // Read if there is nothing.
-    while (buffer.empty()) {
-        // Wait and read.
-        m_listener.wait(clamp(timeout));
-        m_input += m_socket.recv(512);
+    /**
+     * \copydoc Service::prepare
+     */
+    void prepare(fd_set &in, fd_set &out, net::Handle &max) override;
 
-        // Finally try.
-        buffer = util::nextNetwork(m_input);
-    }
-
-    auto value = nlohmann::json::parse(buffer);
-
-    if (!value.is_object())
-        throw std::invalid_argument("invalid message received");
-
-    return value;
-}
+    /**
+     * \copydoc Service::sync
+     */
+    void sync(fd_set &in, fd_set &out) override;
+};
 
 } // !irccd
 
--- a/lib/irccd/irccdctl.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/irccdctl.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -16,67 +16,44 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#include <algorithm>
-#include <cassert>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <unordered_map>
-
 #include <format.h>
 
 #include "command.hpp"
+#include "connection.hpp"
 #include "elapsed-timer.hpp"
 #include "fs.hpp"
 #include "ini.hpp"
 #include "irccdctl.hpp"
-#include "json.hpp"
-#include "net.hpp"
 #include "logger.hpp"
 #include "options.hpp"
 #include "path.hpp"
-#include "sysconfig.hpp"
 #include "system.hpp"
 #include "util.hpp"
 
-namespace irccd {
+using namespace std::string_literals;
 
 using namespace fmt::literals;
 
-using namespace std::placeholders;
-using namespace std::chrono_literals;
-using namespace std::string_literals;
-
-/*
- * Config file format
- * ------------------------------------------------------------------
- *
- * [connect]
- * type = "ip | unix"
- *
- * # if ip
- * host = ""
- * port = number
- * domain = "ipv4 | ipv6", default: ipv4
- *
- * # if unix
- * path = ""
- *
- * [alias]
- * name = replacement
- */
-
-/*
- * Initialize a connection from the configuration file
- * ------------------------------------------------------------------
- */
+namespace irccd {
 
 void Irccdctl::usage() const
 {
-    // TODO: CHANGE
+    bool first = true;
+
+    for (const auto &cmd : m_commandService.commands()) {
+        log::warning() << (first ? "usage: " : "       ") << sys::programName() << " "
+                       << cmd->usage() << std::endl;
+        first = false;
+    }
+
+    std::exit(1);
+}
+
+void Irccdctl::help() const
+{
     log::warning() << "usage: " << sys::programName() << " [options...] <command> [command-options...] [command-args...]\n\n";
     log::warning() << "General options:\n";
-    log::warning() << "\tc, --config file\tspecify the configuration file\n";
+    log::warning() << "\t-c, --config file\tspecify the configuration file\n";
     log::warning() << "\t--help\t\t\tshow this help\n";
     log::warning() << "\t-t, --type type\t\tspecify connection type\n";
     log::warning() << "\t-v, --verbose\t\tbe verbose\n\n";
@@ -85,63 +62,50 @@
     log::warning() << "\t-p, --port port\t\tuse the specified port number\n\n";
     log::warning() << "Available options for type unix (-t, --type):\n";
     log::warning() << "\t-P, --path file\t\tconnect to the specified socket file\n\n";
-    log::warning() << "General commands:\n";
-    log::warning() << "\thelp\t\t\tShow an help topic\n";
-    log::warning() << "\twatch\t\t\tStart listening to irccd\n\n";
-    log::warning() << "Plugin management:\n";
-    log::warning() << "\tplugin-config\t\tGet or set a plugin configuration option\n";
-    log::warning() << "\tplugin-info\t\tGet plugin information\n";
-    log::warning() << "\tplugin-list\t\tList all loaded plugins\n";
-    log::warning() << "\tplugin-load\t\tLoad a plugin\n";
-    log::warning() << "\tplugin-reload\t\tReload a plugin\n";
-    log::warning() << "\tplugin-unload\t\tUnload a plugin\n\n";
-    log::warning() << "Server management:\n";
-    log::warning() << "\tserver-cmode\t\tChange a channel mode\n";
-    log::warning() << "\tserver-cnotice\t\tSend a channel notice\n";
-    log::warning() << "\tserver-connect\t\tConnect to a server\n";
-    log::warning() << "\tserver-disconnect\tDisconnect from a server\n";
-    log::warning() << "\tserver-info\t\tGet server information\n";
-    log::warning() << "\tserver-invite\t\tInvite someone to a channel\n";
-    log::warning() << "\tserver-join\t\tJoin a channel\n";
-    log::warning() << "\tserver-kick\t\tKick someone from a channel\n";
-    log::warning() << "\tserver-list\t\tList all servers\n";
-    log::warning() << "\tserver-me\t\tSend a CTCP Action (same as /me)\n";
-    log::warning() << "\tserver-message\t\tSend a message to someone or a channel\n";
-    log::warning() << "\tserver-mode\t\tChange a user mode\n";
-    log::warning() << "\tserver-notice\t\tSend a private notice\n";
-    log::warning() << "\tserver-nick\t\tChange your nickname\n";
-    log::warning() << "\tserver-part\t\tLeave a channel\n";
-    log::warning() << "\tserver-reconnect\tReconnect one or all servers\n";
-    log::warning() << "\tserver-topic\t\tChange a channel topic\n";
+    log::warning() << "Available commands:\n";
+
+    for (const auto &cmd : m_commandService.commands())
+        log::warning() << "\t" << std::left << std::setw(32)
+                       << cmd->name() << cmd->description() << std::endl;
+
     log::warning() << "\nFor more information on a command, type " << sys::programName() << " help <command>" << std::endl;
+
     std::exit(1);
 }
 
+/*
+ * Configuration file parsing.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * readConnectIp
+ * -------------------------------------------------------------------
+ *
+ * Extract IP connection information from the config file.
+ *
+ * [connect]
+ * type = "ip"
+ * host = "ip or hostname"
+ * port = "port number or service"
+ * domain = "ipv4 or ipv6" (Optional, default: ipv4)
+ */
 void Irccdctl::readConnectIp(const ini::Section &sc)
 {
     ini::Section::const_iterator it;
 
-    // Host.
-    std::string host;
+    std::string host, port;
 
     if ((it = sc.find("host")) == sc.end())
         throw std::invalid_argument("missing host parameter");
 
     host = it->value();
 
-    // Port.
-    std::uint16_t port;
-
     if ((it = sc.find("port")) == sc.end())
         throw std::invalid_argument("missing port parameter");
 
-    try {
-        port = util::toNumber<std::uint16_t>(it->value());
-    } catch (...) {
-        throw std::invalid_argument("invalid port number: " + it->value());
-    }
+    port = it->value();
 
-    // Domain.
     int domain = AF_INET;
 
     if ((it = sc.find("domain")) != sc.end()) {
@@ -153,10 +117,21 @@
             throw std::invalid_argument("invalid domain: " + it->value());
     }
 
-    m_connection =  std::make_unique<ConnectionBase<net::address::Ip>>(net::address::Ip::resolve(host, std::to_string(port), domain));
+    m_address = net::resolveOne(host, port, domain, SOCK_STREAM);
+    m_connection = std::make_unique<Connection>();
 }
 
-void Irccdctl::readConnectUnix(const ini::Section &sc)
+/*
+ * readConnectLocal
+ * -------------------------------------------------------------------
+ *
+ * Extract local connection for Unix.
+ *
+ * [connect]
+ * type = "unix"
+ * path = "path to socket file"
+ */
+void Irccdctl::readConnectLocal(const ini::Section &sc)
 {
 #if !defined(IRCCD_SYSTEM_WINDOWS)
     auto it = sc.find("path");
@@ -164,7 +139,8 @@
     if (it == sc.end())
         throw std::invalid_argument("missing path parameter");
 
-    m_connection = std::make_unique<ConnectionBase<net::address::Local>>(net::address::Local(it->value(), false));
+    m_address = net::local::create(it->value());
+    m_connection = std::make_unique<Connection>();
 #else
     (void)sc;
 
@@ -172,6 +148,12 @@
 #endif
 }
 
+/*
+ * readConnect
+ * -------------------------------------------------------------------
+ *
+ * Generic function for reading the [connect] section.
+ */
 void Irccdctl::readConnect(const ini::Section &sc)
 {
     auto it = sc.find("type");
@@ -182,11 +164,20 @@
     if (it->value() == "ip")
         readConnectIp(sc);
     else if (it->value() == "unix")
-        readConnectUnix(sc);
+        readConnectLocal(sc);
     else
         throw std::invalid_argument("invalid type given: " + it->value());
 }
 
+/*
+ * readGeneral
+ * -------------------------------------------------------------------
+ *
+ * Read the general section.
+ *
+ * [general]
+ * verbose = true
+ */
 void Irccdctl::readGeneral(const ini::Section &sc)
 {
     auto verbose = sc.find("verbose");
@@ -195,65 +186,73 @@
         log::setVerbose(util::isBoolean(verbose->value()));
 }
 
+/*
+ * readAliases
+ * -------------------------------------------------------------------
+ *
+ * Read aliases for irccdctl.
+ *
+ * [alias]
+ * name = ( "command", "arg1, "...", "argn" )
+ */
 void Irccdctl::readAliases(const ini::Section &sc)
 {
-    for (const ini::Option &option : sc) {
+    for (const auto &option : sc) {
         // This is the alias name.
         Alias alias(option.key());
 
-        if (m_commandService.contains(option.key()))
-            throw std::invalid_argument("there is already a command named " + option.key());
-
         // Iterate over the list of commands to execute for this alias.
-        for (const std::string &repl : option) {
+        for (const auto &repl : option) {
             // This is the alias split string.
-            std::vector<std::string> list = util::split(repl, " \t");
+            auto list = util::split(repl, " \t");
 
             if (list.size() < 1)
                 throw std::invalid_argument("alias require at least one argument");
 
             // First argument is the command/alias to execute.
-            std::string command = list[0];
-
-            // This is the alias arguments.
-            std::vector<AliasArg> args;
-
-            for (auto it = list.begin() + 1; it != list.end(); ++it)
-                args.push_back(std::move(*it));
+            auto command = list[0];
 
-            alias.push_back({std::move(command), std::move(args)});
+            // Remove command name and puts arguments.
+            alias.push_back({std::move(command), std::vector<AliasArg>(list.begin() + 1, list.end())});
         }
 
-        // Show for debugging purpose.
-        log::debug("alias {}:"_format(option.key()));
-
-        for (const auto &cmd : alias)
-            log::debug("  {} {}"_format(cmd.command(), util::join(cmd.args().begin(), cmd.args().end(), ' ')));
-
         m_aliases.emplace(option.key(), std::move(alias));
     }
 }
 
-void Irccdctl::read(const std::string &path, const option::Result &options)
+void Irccdctl::read(const std::string &path)
 {
-    ini::Document doc = ini::readFile(path);
-    ini::Document::const_iterator it = doc.find("connect");
+    try {
+        ini::Document doc = ini::readFile(path);
+        ini::Document::const_iterator it;
 
-    // Do not try to read [connect] if specified at command line.
-    if (it != doc.end() && options.count("-t") == 0 && options.count("--type") == 0)
-        readConnect(*it);
-    if ((it = doc.find("general")) != doc.end())
-        readGeneral(*it);
-    if ((it = doc.find("alias")) != doc.end())
-        readAliases(*it);
+        if (!m_connection && (it = doc.find("connect")) != doc.end())
+            readConnect(*it);
+        if ((it = doc.find("general")) != doc.end())
+            readGeneral(*it);
+        if ((it = doc.find("alias")) != doc.end())
+            readAliases(*it);
+    } catch (const std::exception &ex) {
+        log::warning() << path << ": " << ex.what() << std::endl;
+    }
 }
 
 /*
- * Initialize a connection from the command line.
- * ------------------------------------------------------------------
+ * Command line parsing.
+ * -------------------------------------------------------------------
  */
 
-void Irccdctl::parseConnectIp(const option::Result &options, bool ipv6)
+/*
+ * parseConnectIp
+ * ------------------------------------------------------------------
+ *
+ * Parse internet connection from command line.
+ *
+ * -t ip | ipv6
+ * -h host or ip
+ * -p port
+ */
+void Irccdctl::parseConnectIp(const option::Result &options)
 {
     option::Result::const_iterator it;
 
@@ -266,24 +265,34 @@
     host = it->second;
 
     // Port (-p or --port).
-    std::uint16_t port;
+    std::string port;
 
     if ((it = options.find("-p")) == options.end() && (it = options.find("--port")) == options.end())
         throw std::invalid_argument("missing port argument (-p or --port)");
 
-    try {
-        port = util::toNumber<std::uint16_t>(it->second);
-    } catch (...) {
-        throw std::invalid_argument("invalid port number: " + it->second);
-    }
+    port = it->second;
 
     // Domain
-    int domain = (ipv6) ? AF_INET6 : AF_INET;
+    int domain = AF_INET;
 
-    m_connection =  std::make_unique<ConnectionBase<net::address::Ip>>(net::address::Ip::resolve(host, std::to_string(port), domain, SOCK_STREAM));
+    if ((it = options.find("-t")) != options.end())
+        domain = it->second == "ipv6" ? AF_INET6 : AF_INET;
+    else if ((it = options.find("--type")) != options.end())
+        domain = it->second == "ipv6" ? AF_INET6: AF_INET;
+
+    m_address = net::resolveOne(host, port, domain, SOCK_STREAM);
+    m_connection = std::make_unique<Connection>();
 }
 
-void Irccdctl::parseConnectUnix(const option::Result &options)
+/*
+ * parseConnectLocal
+ * ------------------------------------------------------------------
+ *
+ * Parse local connection.
+ *
+ * -P file
+ */
+void Irccdctl::parseConnectLocal(const option::Result &options)
 {
 #if !defined(IRCCD_SYSTEM_WINDOWS)
     option::Result::const_iterator it;
@@ -291,7 +300,8 @@
     if ((it = options.find("-P")) == options.end() && (it = options.find("--path")) == options.end())
         throw std::invalid_argument("missing path parameter (-P or --path)");
 
-    m_connection = std::make_unique<ConnectionBase<net::address::Local>>(net::address::Local(it->second, false));
+    m_address = net::local::create(it->second, false);
+    m_connection = std::make_unique<Connection>();
 #else
     (void)options;
 
@@ -299,6 +309,12 @@
 #endif
 }
 
+/*
+ * parseConnect
+ * ------------------------------------------------------------------
+ *
+ * Generic parsing of command line option for connection.
+ */
 void Irccdctl::parseConnect(const option::Result &options)
 {
     assert(options.count("-t") > 0 || options.count("--type") > 0);
@@ -308,14 +324,14 @@
     if (it == options.end())
         it = options.find("--type");
     if (it->second == "ip" || it->second == "ipv6")
-        return parseConnectIp(options, it->second == "ipv6");
+        return parseConnectIp(options);
     if (it->second == "unix")
-        return parseConnectUnix(options);
+        return parseConnectLocal(options);
 
     throw std::invalid_argument("invalid type given: " + it->second);
 }
 
-option::Result Irccdctl::parse(int &argc, char **&argv) const
+option::Result Irccdctl::parse(int &argc, char **&argv)
 {
     // 1. Parse command line options.
     option::Options def{
@@ -354,6 +370,41 @@
     return result;
 }
 
+nlohmann::json Irccdctl::next(const std::string id)
+{
+    ElapsedTimer timer;
+
+    while (m_input.empty() && m_connection->isConnected() && timer.elapsed() < m_timeout)
+        m_connection->poll();
+
+    if (m_input.empty())
+        return nlohmann::json();
+
+    nlohmann::json value;
+
+    if (id == "") {
+        value = m_input[0];
+        m_input.erase(m_input.begin());
+    } else {
+        auto it = std::find_if(m_input.begin(), m_input.end(), [&] (const auto &v) {
+            auto rt = v.find("response");
+
+            if (rt != v.end() && rt->is_string() && *rt == id)
+                return true;
+
+            return false;
+        });
+
+        // Remove the previous messages.
+        if (it != m_input.end()) {
+            value = *it;
+            m_input.erase(m_input.begin(), it);
+        }
+    }
+
+    return value;
+}
+
 void Irccdctl::exec(const Command &cmd, std::vector<std::string> args)
 {
     // 1. Build options from command line arguments.
@@ -394,10 +445,10 @@
     request.push_back({"command", cmd.name()});
 
     // 5. Send the command.
-    m_connection->send(request.dump(), 30000);
+    m_connection->request(request);
 
     // 6. Parse the result.
-    cmd.result(*this, m_connection->next(cmd.name(), 30000));
+    cmd.result(*this, next(cmd.name()));
 }
 
 void Irccdctl::exec(const Alias &alias, std::vector<std::string> argsCopy)
@@ -458,36 +509,10 @@
     }
 }
 
-void Irccdctl::connect()
-{
-    log::info("{}: connecting to irccd..."_format(sys::programName()));
-
-    // Try to connect.
-    m_connection->connect(30000);
-
-    // Get irccd information.
-    auto object = m_connection->next(30000);
-    auto program = object.find("program");
-
-    if (program == object.end() || !program->is_string() || program->get<std::string>() != "irccd")
-        throw std::runtime_error("not an irccd server");
-
-    // Get values.
-    m_major = util::json::toInt(object["major"]);
-    m_minor = util::json::toInt(object["minor"]);
-    m_patch = util::json::toInt(object["patch"]);
-    m_javascript = util::json::toBool(object["javascript"]);
-    m_ssl = util::json::toBool(object["ssl"]);
-
-    log::info() << std::boolalpha;
-    log::info("{}: connected to irccd {}.{}.{}"_format(sys::programName(), m_major, m_minor, m_patch));
-    log::info("{}: javascript: {}, ssl support: {}"_format(sys::programName(), m_javascript, m_ssl));
-}
-
 void Irccdctl::run(int argc, char **argv)
 {
     // 1. Read command line arguments.
-    option::Result result = parse(argc, argv);
+    auto result = parse(argc, argv);
 
     /*
      * 2. Open optional config by command line or by searching it
@@ -505,17 +530,19 @@
         auto it = result.find("-c");
 
         if (it != result.end() || (it = result.find("--config")) != result.end())
-            read(it->second, result);
+            read(it->second);
         else {
             for (const std::string &dir : path::list(path::PathConfig)) {
                 std::string path = dir + "irccdctl.conf";
 
-                if (fs::exists(path))
-                    read(path, result);
+                if (fs::exists(path)) {
+                    read(path);
+                    break;
+                }
             }
         }
     } catch (const std::exception &ex) {
-        log::warning("{}: {}"_format(sys::programName(), ex.what()));
+        log::warning() << sys::programName() << ": " << ex.what() << std::endl;
         std::exit(1);
     }
 
@@ -531,8 +558,23 @@
             std::exit(1);
         }
 
-        connect();
-    }
+        m_connection->onDisconnect.connect([this] (auto reason) {
+            log::warning() << "connection lost to irccd: " << reason << std::endl;
+        });
+        m_connection->onConnect.connect([this] (auto info) {
+            log::info() << "connected to irccd "
+                        << info.major << "."
+                        << info.minor << "."
+                        << info.patch << std::endl;
+        });
+        m_connection->onMessage.connect([this] (auto msg) {
+            m_input.push_back(std::move(msg));
+        });
+
+        m_connection->connect(m_address);
+    } else if (argc == 1)
+        help();
+        // NOTREACHED
 
     // Build a vector of arguments.
     std::vector<std::string> args;
--- a/lib/irccd/irccdctl.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/irccdctl.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -24,22 +24,24 @@
  * \brief Base class for irccdctl front end.
  */
 
-#include <cassert>
 #include <map>
 #include <memory>
 #include <string>
 
+#include "connection.hpp"
 #include "alias.hpp"
-#include "connection.hpp"
 #include "options.hpp"
 #include "service-command.hpp"
 
+#include <json.hpp>
+
 namespace irccd {
 
-class Command;
+class Connection;
 
 namespace ini {
 
+class Document;
 class Section;
 
 } // !ini
@@ -49,36 +51,36 @@
  */
 class Irccdctl {
 private:
-    // Irccd's information.
-    unsigned short m_major{0};
-    unsigned short m_minor{0};
-    unsigned short m_patch{0};
-
-    // Irccd's compilation option.
-    bool m_javascript{true};
-    bool m_ssl{true};
-
     // Commands.
     CommandService m_commandService;
 
+    // Connection handler.
     std::unique_ptr<Connection> m_connection;
-    std::unordered_map<std::string, Alias> m_aliases;
+    std::uint32_t m_timeout{30000};
+    net::Address m_address;
+
+    // Aliases.
+    std::map<std::string, Alias> m_aliases;
+
+    // Incoming data.
+    std::vector<nlohmann::json> m_input;
 
     void usage() const;
+    void help() const;
 
+    // Parse configuration file.
     void readConnectIp(const ini::Section &sc);
-    void readConnectUnix(const ini::Section &sc);
+    void readConnectLocal(const ini::Section &sc);
     void readConnect(const ini::Section &sc);
     void readGeneral(const ini::Section &sc);
     void readAliases(const ini::Section &sc);
-    void read(const std::string &path, const option::Result &options);
+    void read(const std::string &path);
 
-    void parseConnectIp(const option::Result &options, bool ipv6);
-    void parseConnectUnix(const option::Result &options);
+    // Parse command line options.
+    void parseConnectIp(const option::Result &options);
+    void parseConnectLocal(const option::Result &options);
     void parseConnect(const option::Result &options);
-    option::Result parse(int &argc, char **&argv) const;
-
-    void connect();
+    option::Result parse(int &argc, char **&argv);
 
 public:
     /**
@@ -91,6 +93,30 @@
         return m_commandService;
     }
 
+    inline const Connection &connection() const noexcept
+    {
+        return *m_connection;
+    }
+
+    inline Connection &connection() noexcept
+    {
+        return *m_connection;
+    }
+
+    /**
+     * Get the next response with the given id.
+     *
+     * If the response id is not provided, get the next incoming message.
+     *
+     * Otherwise, if the id is provided, all other previous messages will be
+     * discarded.
+     *
+     * \param id the response id (e.g. server-message)
+     * \return the next message
+     * \warning this may skip previous events
+     */
+    IRCCD_EXPORT nlohmann::json next(const std::string id = "");
+
     /**
      * Execute the given command and wait for its result.
      *
@@ -115,16 +141,6 @@
     IRCCD_EXPORT void exec(std::vector<std::string> args);
 
     /**
-     * Get the connection.
-     *
-     * \return the connection
-     */
-    inline Connection &connection() noexcept
-    {
-        return *m_connection;
-    }
-
-    /**
      * Run the irccdctl front end.
      *
      * \param argc the number of arguments
--- a/lib/irccd/net.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/net.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -1,5 +1,5 @@
 /*
- * net.hpp -- portable C++ socket wrappers
+ * net.hpp -- portable C++ socket wrapper
  *
  * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
  *
@@ -63,36 +63,53 @@
 /**
  * \page Networking Networking
  *
- *   - \subpage net-configuration
  *   - \subpage net-options
  *   - \subpage net-concepts
  */
 
 /**
- * \page net-configuration Configuration
+ * \page net-options User options
+ *
+ * The user may set the following variables before compiling these files:
+ *
+ * # General options
+ *
+ * - **NET_NO_AUTO_INIT**: (bool) Set to 0 if you don't want Socket class to
+ *   automatically calls init function and finish functions.
+ *
+ * - **NET_NO_SSL**: (bool) Set to 0 if you don't have access to OpenSSL
+ *   library.
+ *
+ * - **NET_NO_AUTO_SSL_INIT**: (bool) Set to 0 if you don't want Socket class
+ *   with Tls to automatically init the OpenSSL library. You will need to call
+ *   ssl::init and ssl::finish.
  *
  * # General compatibility options.
  *
- * The following options are auto detected but you can override them if you want.
+ * 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
+ * - **NET_HAVE_INET_PTON**: (bool) Set to 1 if you have inet_pton function.
+ *   True for all platforms and Windows
  *   if _WIN32_WINNT is greater or equal to 0x0600.
  *
  * - **NET_HAVE_INET_NTOP**: (bool) Same as above.
  *
- * **Note:** On Windows, it is highly encouraged to set _WIN32_WINNT to at least 0x0600 on MinGW.
+ * **Note:** On Windows, it is highly encouraged to set _WIN32_WINNT to at least
+ * 0x0600 on MinGW.
  *
  * # Options for Listener class
  *
- * Feature detection, multiple implementations may be avaible, for example, Linux has poll, select and epoll.
+ * 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.
+ * 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`).
@@ -108,25 +125,8 @@
  */
 
 /**
- * \page net-options User options
- *
- * The user may set the following variables before compiling these files:
- *
- * # General options
- *
- * - **NET_NO_AUTO_INIT**: (bool) Set to 0 if you don't want Socket class to
- * automatically calls 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.
- */
-
-/**
  * \page net-concepts Concepts
  *
- *   - \subpage net-concept-address
  *   - \subpage net-concept-backend
  *   - \subpage net-concept-option
  *   - \subpage net-concept-stream
@@ -134,81 +134,13 @@
  */
 
 /**
- * \page net-concept-address Address (Concept)
- *
- * An address is used in many place for creating, binding, connecting, receiving and sending. They are implemented as
- * templates to allow any type of address and to make sure the same address is used for a given socket.
- *
- * This concepts requires the following functions:
- *
- * # Address (constructor)
- *
- * The address must have the following constructor overloads.
- *
- * **Note**: the user can add custom constructors.
- *
- * ## Synopsis
- *
- * ````
- * Address(); // (0);
- * Address(const sockaddr *sa, socklen_t length); // (1)
- * ````
- *
- * ## Arguments
- *
- *   - **ss**: the storage to construct from,
- *   - **length**: the storage length.
- *
- * # domain
- *
- * Get the domain (e.g. AF_INET).
- *
- * ## Synopsis
- *
- * ````
- * int domain() const noexcept;
- * ````
- *
- * ## Returns
- *
- * The domain.
- *
- * # address
- *
- * Get the underlying address.
- *
- * ## Synopsis
- *
- * ````
- * const sockaddr *address() const noexcept;
- * ````
- *
- * ## Returns
- *
- * The address.
- *
- * # length
- *
- * Get the underlying length.
- *
- * ## Synopsis
- *
- * ````
- * socklen_t length() const noexcept;
- * ````
- *
- * ## Returns
- *
- * The length.
- */
-
-/**
  * \page net-concept-backend Backend (Concept)
  *
- * A backend is an interface for the Listener class. It is primarily designed to be the most suitable for the host
- * environment.
+ * A backend is an interface for the Listener class. It is primarily designed to
+ * be the most suitable for the host environment.
  *
- * The backend must be default constructible, it is highly encouraged to be move constructible.
+ * The backend must be default constructible, it is highly encouraged to be move
+ * constructible.
  *
  * This concepts requires the following functions:
  *
@@ -233,7 +165,8 @@
  * ## Synopsis
  *
  * ````
- * void set(const ListenerTable &table, Handle handle, Condition condition, bool add);
+ * void set(const ListenerTable &table, Handle handle, Condition condition,
+ * bool add);
  * ````
  *
  * ## Arguments
@@ -241,7 +174,7 @@
  *   - **table**: the current table of sockets,
  *   - **handle**: the handle to set,
  *   - **condition**: the condition to add (may be OR'ed),
- *   - **add**: hint set to true if the handle is not currently registered at all.
+ *   - **add**: hint set to true if the handle is not currently registered.
  *
  * # unset
  *
@@ -250,7 +183,8 @@
  * ## Synopsis
  *
  * ````
- * void unset(const ListenerTable &table, Handle handle, Condition condition, bool remove);
+ * void unset(const ListenerTable &table, Handle handle, Condition condition,
+ * bool remove);
  * ````
  *
  * ## Arguments
@@ -285,7 +219,8 @@
  *
  * An option can be set or get from a socket.
  *
- * If an operation is not available, provides the function but throws an exception with ENOSYS errno code.
+ * If an operation is not available, provides the function but throws an
+ * exception with ENOSYS message.
  *
  * This concepts requires the following functions:
  *
@@ -306,8 +241,8 @@
  * ## Synopsis
  *
  * ````
- * template <typename Address, typename Protocol>
- * inline void set(Socket<Address, Protocol> &sc) const;
+ * template <typename Address>
+ * void set(Socket &sc) const;
  * ````
  *
  * ## Arguments
@@ -321,8 +256,8 @@
  * ## Synopsis
  *
  * ````
- * template <typename Address, typename Protocol>
- * inline bool get(Socket<Address, Protocol> &sc) const;
+ * template <typename Address>
+ * T get(Socket &sc) const;
  * ````
  *
  * ## Arguments
@@ -353,95 +288,49 @@
  *
  * The type of socket.
  *
- * # create
- *
- * Function called immediately after creation of socket. Interface must provides this function even if the
- * interface does not require anything.
- *
- * ## Synopsis
- *
- * ````
- * template <typename Address>
- * void create(Socket<Address, Tcp> &) const noexcept;
- * ````
- *
  * # connect
  *
- * Initial connect function.
- *
- * In this function, the interface receive the socket, address and a condition (initially set to None). If the
- * underlying socket is marked non-blocking and the operation would block, the interface must set the condition
- * to the required one.
+ * Connect to the given address.
  *
  * ## Synopsis
  *
  * ````
- * template <typename Address, typename Protocol>
- * void connect(Socket<Address, Protocol> &sc, const sockaddr *address, socklen_t length, Condition &cond);
+ * void connect(const sockaddr *address, socklen_t length); // (0)
+ * void connect(const Address &address); // 1 (Optional)
  * ````
  *
  * ## Arguments
  *
- *   - **sc**: the socket,
  *   - **address**: the address,
- *   - **length**: the address length,
- *   - **cond**: the condition to update.
- *
- * # resumeConnect
- *
- * Continue the connection.
+ *   - **length**: the address length.
  *
- * ## Synopsis
+ * ## Throws
  *
- * ````
- * template <typename Address, typename Protocol>
- * void resumeConnect(Socket<Address, Protocol> &sc, Condition &cond)
- * ````
- *
- * ## Arguments
- *
- *   - **sc**: the socket,
- *   - **cond**: the condition to update.
+ *   - net::WouldBlockError: if the operation would block,
+ *   - net::Error: on other error.
  *
  * # accept
  *
  * Accept a new client.
  *
- * If the interface has no pending connection, an invalid socket SHOULD be returned, otherwise return the client and
- * set the condition if the accept process is not complete yet.
- *
- * The interface MUST stores the client information into address and length parameters, they are guaranted to never be
- * null.
+ * If no pending connection is available and operation would block, the
+ * implementation must throw WouldBlockError. Any other error can be thrown
+ * otherwise a valid socket must be returned.
  *
  * ## Synopsis
  *
  * ````
- * template <typename Address, typename Protocol>
- * Socket<Address, Protocol> accept(Socket<Address, Protocol> &sc, sockaddr *address, socklen_t *length, Condition &cond);
+ * Socket accept();
  * ````
  *
- * ## Arguments
+ * ## Returns
  *
- *   - **sc**: the socket,
- *   - **address**: the information address,
- *   - **length**: the address initial length (sockaddr_storage),
- *   - **cond**: the condition to update.
- *
- * # resumeAccept
- *
- * Continue the accept process on the returned client.
+ * The new socket.
  *
- * ## Synopsis
+ * ## Throws
  *
- * ````
- * template <typename Address, typename Protocol>
- * void accept(Socket<Address, Protocol> &sc, Condition &cond) const noexcept;
- * ````
- *
- * ## Arguments
- *
- *   - **sc**: the socket,
- *   - **cond**: the condition to update.
+ *   - net::WouldBlockError: if the operation would block,
+ *   - net::Error: on other error.
  *
  * # recv
  *
@@ -450,21 +339,23 @@
  * ## Synopsis
  *
  * ````
- * template <typename Address>
- * std::size_t recv(Socket<Address, Tcp> &sc, void *data, std::size_t length, Condition &cond);
+ * unsigned recv(void *data, unsigned length);
  * ````
  *
  * ## Arguments
  *
- *   - **sc**: the socket,
  *   - **data**: the destination buffer,
- *   - **length**: the destination buffer length,
- *   - **cond**: the condition to update.
+ *   - **length**: the destination buffer length.
  *
  * ## Returns
  *
  * The number of bytes sent.
  *
+ * ## Throws
+ *
+ *   - net::WouldBlockError: if the operation would block,
+ *   - net::Error: on other error.
+ *
  * # send
  *
  * Send data.
@@ -472,20 +363,22 @@
  * ## Synopsis
  *
  * ````
- * template <typename Address>
- * std::size_t send(Socket<Address, Tcp> &sc, const void *data, std::size_t length, Condition &cond);
+ * unsigned send(const void *data, unsigned length);
  * ````
  *
  * ## Arguments
  *
- *   - **sc**: the socket,
  *   - **data**: the data to send,
- *   - **length**: the data length,
- *   - **cond**: the condition to update.
+ *   - **length**: the data length.
  *
  * ## Returns
  *
  * The number of bytes sent.
+ *
+ * ## Throws
+ *
+ *   - net::WouldBlockError: if the operation would block,
+ *   - net::Error: on other error.
  */
 
 /**
@@ -514,42 +407,50 @@
  * ## Synopsis
  *
  * ````
- * template <typename Address, typename Protocol>
- * std::size_t recvfrom(Socket<Address, Protocol> &sc, void *data, std::size_t length, sockaddr *address, socklen_t *addrlen, Condition &cond);
+ * unsigned recvfrom(void *data, unsigned length, sockaddr *address,
+ *     socklen_t *addrlen);
+ * unsigned recvfrom(void *data, unsigned length, Address *source)
  * ````
  *
  * ## Arguments
  *
- *   - **sc**: the socket,
  *   - **data**: the data,
  *   - **length**: the length,
  *   - **address**: the source address,
- *   - **addrlen**: the source address in/out length,
- *   - **cond**: the condition.
+ *   - **addrlen**: the source address in/out length.
  *
  * ## Returns
  *
  * The number of bytes received.
  *
+ * ## Throws
+ *
+ *   - net::WouldBlockError: if the operation would block,
+ *   - net::Error: on other error.
+ *
  * # sendto
  *
  * ````
- * template <typename Address, typename Protocol>
- * std::size_t sendto(Socket<Address, Protocol> &sc, const void *data, std::size_t length, const sockaddr *address, socklen_t addrlen, Condition &cond);
+ * unsigned sendto(const void *data, unsigned length, const sockaddr *address,
+ *     socklen_t addrlen);
+ * unsigned sendto(const void *data, unsigned length, const Address &address);
  * ````
  *
  * ## Arguments
  *
- *   - **sc**: the socket,
  *   - **data**: the data to send,
  *   - **length**: the data length,
  *   - **address**: the destination address,
- *   - **addrlen**: the destination address length,
- *   - **cond**: the condition.
+ *   - **addrlen**: the destination address length.
  *
  * ## Returns
  *
  * The number of bytes sent.
+ *
+ * ## Throws
+ *
+ *   - net::WouldBlockError: if the operation would block,
+ *   - net::Error: on other error.
  */
 
 /*
@@ -557,24 +458,27 @@
  * ------------------------------------------------------------------
  */
 
-// Include Windows headers before because it brings _WIN32_WINNT if not specified by the user.
+/*
+ * Include Windows headers before because it brings _WIN32_WINNT if not
+ * specified by the user.
+ */
 #if defined(_WIN32)
-#  include <WinSock2.h>
-#  include <WS2tcpip.h>
+#   include <WinSock2.h>
+#   include <WS2tcpip.h>
 #else
-#  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>
+#   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>
@@ -613,23 +517,23 @@
  */
 
 #if defined(_WIN32)
-#  if _WIN32_WINNT >= 0x0600 && !defined(NET_HAVE_POLL)
-#    define NET_HAVE_POLL
-#  endif
+#   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
+#   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
+#   if !defined(NET_HAVE_EPOLL)
+#       define NET_HAVE_EPOLL
+#   endif
+#   if !defined(NET_HAVE_POLL)
+#       define NET_HAVE_POLL
+#   endif
 #endif
 
 /*
@@ -641,26 +545,26 @@
  * \brief Tells if inet_pton is available
  */
 #if !defined(NET_HAVE_INET_PTON)
-#  if defined(_WIN32)
-#    if _WIN32_WINNT >= 0x0600
-#      define NET_HAVE_INET_PTON
-#    endif
-#  else
-#    define NET_HAVE_INET_PTON
-#  endif
+#   if defined(_WIN32)
+#       if _WIN32_WINNT >= 0x0600
+#           define NET_HAVE_INET_PTON
+#       endif
+#   else
+#       define NET_HAVE_INET_PTON
+#   endif
 #endif
 
 /**
  * \brief Tells if inet_ntop is available
  */
 #if !defined(NET_HAVE_INET_NTOP)
-#  if defined(_WIN32)
-#    if _WIN32_WINNT >= 0x0600
-#      define NET_HAVE_INET_NTOP
-#    endif
-#  else
-#    define NET_HAVE_INET_NTOP
-#  endif
+#   if defined(_WIN32)
+#       if _WIN32_WINNT >= 0x0600
+#           define NET_HAVE_INET_NTOP
+#       endif
+#   else
+#       define NET_HAVE_INET_NTOP
+#   endif
 #endif
 
 /*
@@ -674,35 +578,35 @@
  * \brief Defines the default backend
  */
 #if defined(_WIN32)
-#  if !defined(NET_DEFAULT_BACKEND)
-#    if defined(NET_HAVE_POLL)
-#      define NET_DEFAULT_BACKEND Poll
-#    else
-#      define NET_DEFAULT_BACKEND Select
-#    endif
-#  endif
+#   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
+#   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
+#   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
+#   if !defined(NET_DEFAULT_BACKEND)
+#       define NET_DEFAULT_BACKEND Select
+#   endif
 #endif
 
 #if defined(NET_HAVE_POLL) && !defined(_WIN32)
-#  include <poll.h>
+#    include <poll.h>
 #endif
 
 namespace irccd {
@@ -759,7 +663,8 @@
  * Portable constants.
  * ------------------------------------------------------------------
  *
- * These constants are needed to check functions return codes, they are rarely needed in end user code.
+ * These constants are needed to check functions return codes, they are rarely
+ * needed in end user code.
  */
 
 #if defined(_WIN32)
@@ -799,7 +704,8 @@
 }
 
 /**
- * Initialize the socket library. Except if you defined NET_NO_AUTO_INIT, you don't need to call this
+ * Initialize the socket library. Except if you defined NET_NO_AUTO_INIT, you
+ * don't need to call this
  * function manually.
  */
 inline void init() noexcept
@@ -816,7 +722,10 @@
         WSADATA wsa;
         WSAStartup(MAKEWORD(2, 2), &wsa);
 
-        // If NET_NO_AUTO_INIT is not set then the user must also call finish himself.
+        /*
+         * If NET_NO_AUTO_INIT is not set then the user must also call finish
+         * himself.
+         */
 #if !defined(NET_NO_AUTO_INIT)
         atexit(finish);
 #endif
@@ -856,7 +765,8 @@
 }
 
 /**
- * Get the last socket system error. The error is set from errno or from WSAGetLastError on Windows.
+ * Get the last socket system error. The error is set from errno or from
+ * WSAGetLastError on Windows.
  *
  * \return a string message
  */
@@ -871,6 +781,9 @@
 
 #if !defined(NET_NO_SSL)
 
+/**
+ * \brief SSL namespace
+ */
 namespace ssl {
 
 /**
@@ -878,13 +791,13 @@
  * \brief Which OpenSSL method to use.
  */
 enum Method {
-    Tlsv1,        //!< TLS v1.2 (recommended)
-    Sslv3        //!< SSLv3
+    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.
+ * Initialize the OpenSSL library. Except if you defined NET_NO_AUTO_SSL_INIT,
+ * you don't need to call this function manually.
  */
 inline void init() noexcept
 {
@@ -926,95 +839,118 @@
  */
 
 /**
- * \brief Base class for sockets 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;
+    std::string m_message;
 
 public:
     /**
-     * Constructor that use the last system error.
+     * Construct the error using the specified error from the system.
      *
-     * \param code which kind of error
-     * \param function the function name
+     * \param code the error code
+     * \warning the code must be a Winsock error or errno on Unix
      */
-    inline Error(Code code, std::string function)
-        : m_code(code)
-        , m_function(std::move(function))
-        , m_error(error())
+    inline Error(int code) noexcept
+        : m_message(error(code))
     {
     }
 
     /**
-     * Constructor that use the system error set by the user.
+     * Construct the error using the custom message.
      *
-     * \param code which kind of error
-     * \param function the function name
-     * \param n the error
+     * \param message the message
      */
-    inline Error(Code code, std::string function, int n)
-        : m_code(code)
-        , m_function(std::move(function))
-        , m_error(error(n))
+    inline Error(std::string message) noexcept
+        : m_message(std::move(message))
     {
     }
 
     /**
-     * Constructor that set the error specified by the user.
-     *
-     * \param code which kind of error
-     * \param function the function name
-     * \param error the error
+     * Construct the error using the last message from the system.
      */
-    inline Error(Code code, std::string function, std::string error)
-        : m_code(code)
-        , m_function(std::move(function))
-        , m_error(std::move(error))
+    inline Error() noexcept
+#if defined(_WIN32)
+        : Error(WSAGetLastError())
+#else
+        : Error(errno)
+#endif
     {
     }
 
     /**
-     * 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 override
     {
-        return m_error.c_str();
+        return m_message.c_str();
+    }
+};
+
+/**
+ * \brief Timeout occured.
+ */
+class TimeoutError : public std::exception {
+public:
+    /**
+     * Get the error message.
+     *
+     * \return the message
+     */
+    const char *what() const noexcept override
+    {
+        return std::strerror(ETIMEDOUT);
+    }
+};
+
+/**
+ * \brief Operation would block.
+ */
+class WouldBlockError : public std::exception {
+public:
+    /**
+     * Get the error message.
+     *
+     * \return the message
+     */
+    const char *what() const noexcept override
+    {
+        return std::strerror(EWOULDBLOCK);
+    }
+};
+
+/**
+ * \brief Operation requires sending data to complete.
+ */
+class WantWriteError : public std::exception {
+public:
+    /**
+     * Get the error message.
+     *
+     * \return the message
+     */
+    const char *what() const noexcept override
+    {
+        return "operation requires writing to complete";
+    }
+};
+
+/**
+ * \brief Operation requires reading data to complete.
+ */
+class WantReadError : public std::exception {
+public:
+    /**
+     * Get the error message.
+     *
+     * \return the message
+     */
+    const char *what() const noexcept override
+    {
+        return "operation requires read to complete";
     }
 };
 
@@ -1028,14 +964,11 @@
 /**
  * \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
+    None,                       //!< No condition is required
+    Readable = (1 << 0),        //!< The socket must be readable
+    Writable = (1 << 1)         //!< The socket must be writable
 };
 
 /**
@@ -1127,1695 +1060,38 @@
     return v1;
 }
 
-/*
- * Base Socket class
- * ------------------------------------------------------------------
- *
- * This base class has operations that are common to all types of sockets but you usually instanciate
- * a SocketTcp or SocketUdp
+/**
+ * \brief Generic socket address storage.
+ * \ingroup net-module-addresses
  */
-
-/**
- * \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 {
+class Address {
 private:
-    Protocol m_proto;
-
-protected:
-    /**
-     * The native handle.
-     */
-    Handle m_handle{Invalid};
+    sockaddr_storage m_storage;
+    socklen_t m_length;
 
 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
+     * Construct empty address.
      */
-    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 address which type of address
-     * \param protocol the protocol
-     * \throw net::Error on errors
-     */
-    explicit inline Socket(const Address &address = {}, Protocol protocol = {})
-        : Socket(address.domain(), protocol.type(), 0, std::move(protocol))
-    {
-    }
-
-    /**
-     * Create the socket with an already defined handle and its protocol.
-     *
-     * \param handle the handle
-     * \param protocol the 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)
+    inline Address() noexcept
+        : m_storage{}
+        , m_length(0)
     {
     }
 
     /**
-     * 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)
-    {
-        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
-     * \return the value
-     * \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.
+     * Construct address from existing one.
      *
-     * \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
-     */
-    Address getsockname() 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(reinterpret_cast<sockaddr *>(&ss), length);
-    }
-
-    /**
-     * Get connected address. This is a wrapper for getpeername().
-     *
-     * \pre isOpen()
-     * \return the address
-     * \throw Error on failures
-     */
-    Address getpeername() const
-    {
-        assert(m_handle != Invalid);
-
-        sockaddr_storage ss;
-        socklen_t length = sizeof (sockaddr_storage);
-
-        if (::getpeername(m_handle, reinterpret_cast<sockaddr *>(&ss), &length) == Failure)
-            throw Error(Error::System, "getpeername");
-
-        return Address(reinterpret_cast<sockaddr *>(&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
-     */
-    inline 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()
+     * \pre address != nullptr
      * \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);
-    }
-
-    /**
-     * Continue connect process.
-     *
-     * \pre isOpen()
-     * \param cond the condition require for next selection
-     * \throw net::Error on failures
-     */
-    inline void resumeConnect(Condition &cond)
-    {
-        assert(m_handle != Invalid);
-
-        cond = Condition::None;
-
-        m_proto.resumeConnect(*this, cond);
-    }
-
-    /**
-     * Continue connect process.
-     *
-     * \pre isOpen()
-     * \throw net::Error on failures
-     */
-    inline void resumeConnect()
-    {
-        Condition dummy;
-
-        resumeConnect(dummy);
-    }
-
-    /**
-     * Accept a new client.
-     *
-     * If no connection is available immediately, returns an invalid socket.
-     *
-     * \pre isOpen()
-     * \param address the client information
-     * \param cond the condition to wait to complete accept on the **client**
-     * \return the new client or an invalid if no client is immediately available
-     * \throw net::Error on failures
-     */
-    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(reinterpret_cast<sockaddr *>(&storage), length);
-
-        return client;
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param address the client information
-     * \return the new client or an invalid if no client is immediately available
-     * \throw net::Error on failures
-     */
-    inline Socket<Address, Protocol> accept(Address &address)
-    {
-        Condition dummy;
-
-        return accept(address, dummy);
-    }
-
-    /**
-     * Overlaoded function.
-     *
-     * \pre isOpen()
-     * \return the new client or an invalid if no client is immediately available
-     * \throw net::Error on failures
-     */
-    inline Socket<Address, Protocol> accept()
-    {
-        Address da;
-        Condition dc;
-
-        return accept(da, dc);
-    }
-
-    /**
-     * Continue accept process.
-     *
-     * \pre isOpen()
-     * \param cond the condition
-     * \throw net::Error on failures
-     * \note This should be called on the returned client from accept
-     */
-    inline void resumeAccept(Condition &cond)
-    {
-        assert(m_handle != Invalid);
-
-        cond = Condition::None;
-
-        m_proto.resumeAccept(*this, cond);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \throw net::Error on failures
-     */
-    inline void resumeAccept()
-    {
-        Condition dummy;
-
-        resumeAccept(dummy);
-    }
-
-    /**
-     * Receive some data.
-     *
-     * \pre isOpen()
-     * \param data the destination buffer
-     * \param length the data length
-     * \param cond the condition
-     * \return the number of bytes received
-     * \throw net::Error on failures
-     */
-    inline std::size_t recv(void *data, std::size_t length, Condition &cond)
-    {
-        assert(m_handle != Invalid);
-
-        cond = Condition::None;
-
-        return m_proto.recv(*this, data, length, cond);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the destination buffer
-     * \param length the data length
-     * \return the number of bytes received
-     * \throw net::Error on failures
-     */
-    inline std::size_t recv(void *data, std::size_t length)
-    {
-        Condition dummy;
-
-        return recv(data, length, dummy);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param count number of bytes desired
-     * \param cond the condition
-     * \return the result string
-     * \throw net::Error on failures
-     */
-    std::string recv(std::size_t 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;
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param count number of bytes desired
-     * \return the result string
-     * \throw net::Error on failures
-     */
-    inline std::string recv(std::size_t count)
-    {
-        Condition dummy;
-
-        return recv(count, dummy);
-    }
-
-    /**
-     * Send some data.
-     *
-     * \pre isOpen()
-     * \param data the data to send
-     * \param length the length
-     * \param cond the condition
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t send(const void *data, std::size_t length, Condition &cond)
-    {
-        assert(m_handle != Invalid);
-
-        cond = Condition::None;
-
-        return m_proto.send(*this, data, length, cond);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the data to send
-     * \param length the length
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t send(const void *data, std::size_t length)
-    {
-        Condition dummy;
-
-        return send(data, length, dummy);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the data to send
-     * \param cond the condition
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t send(const std::string &data, Condition &cond)
-    {
-        return send(data.c_str(), data.length(), cond);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the data to send
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t send(const std::string &data)
-    {
-        Condition dummy;
-
-        return send(data.c_str(), data.length(), dummy);
-    }
-
-    /**
-     * Send some data to the given client.
-     *
-     * \pre isOpen()
-     * \param data the data
-     * \param length the length
-     * \param address the client address
-     * \param addrlen the client address length
-     * \param cond the condition
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t sendto(const void *data, std::size_t 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);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the data
-     * \param length the length
-     * \param address the client address
-     * \param addrlen the client address length
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t sendto(const void *data, std::size_t length, const sockaddr *address, socklen_t addrlen)
-    {
-        Condition dummy;
-
-        return send(data, length, address, addrlen, dummy);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the data
-     * \param length the length
-     * \param address the client address
-     * \param cond the condition
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t sendto(const void *data, std::size_t length, const Address &address, Condition &cond)
-    {
-        return sendto(data, length, address.address(), address.length(), cond);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the data
-     * \param length the length
-     * \param address the client address
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t sendto(const void *data, std::size_t length, const Address &address)
-    {
-        Condition dummy;
-
-        return sendto(data, length, address.address(), address.length(), dummy);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the data
-     * \param cond the condition
-     * \param address the client address
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t sendto(const std::string &data, const Address &address, Condition &cond)
-    {
-        return sendto(data.c_str(), data.length(), address.address(), address.length(), cond);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the data
-     * \param address the client address
-     * \return the number of bytes sent
-     * \throw net::Error on failures
-     */
-    inline std::size_t sendto(const std::string &data, const Address &address)
-    {
-        Condition dummy;
-
-        return sendto(data.c_str(), data.length(), address.address(), address.length(), dummy);
-    }
-
-    /**
-     * Receive some data from a client.
-     *
-     * \pre isOpen()
-     * \param data the destination buffer
-     * \param length the buffer length
-     * \param address the client information
-     * \param addrlen the client address initial length
-     * \param cond the condition
-     * \return the number of bytes received
-     * \throw net::Error on failures
-     */
-    inline std::size_t recvfrom(void *data, std::size_t 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);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the destination buffer
-     * \param length the buffer length
-     * \param address the client information
-     * \param addrlen the client address initial length
-     * \return the number of bytes received
-     * \throw net::Error on failures
-     */
-    inline std::size_t recvfrom(void *data, std::size_t length, sockaddr *address, socklen_t *addrlen)
-    {
-        Condition dummy;
-
-        return recvfrom(data, length, address, addrlen, dummy);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the destination buffer
-     * \param length the buffer length
-     * \param address the client information
-     * \param cond the condition
-     * \return the number of bytes received
-     * \throw net::Error on failures
-     */
-    std::size_t recvfrom(void *data, std::size_t 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 && cond == Condition::None)
-            address = Address(reinterpret_cast<sockaddr *>(&storage), addrlen);
-
-        return n;
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the destination buffer
-     * \param length the buffer length
-     * \param address the client information
-     * \return the number of bytes received
-     * \throw net::Error on failures
-     */
-    inline std::size_t recvfrom(void *data, std::size_t length, Address &address)
-    {
-        Condition dummy;
-
-        return recvfrom(data, length, address, dummy);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param data the destination buffer
-     * \param length the buffer length
-     * \return the number of bytes received
-     * \throw net::Error on failures
-     */
-    inline std::size_t recvfrom(void *data, std::size_t length)
-    {
-        Address da;
-        Condition dc;
-
-        return recvfrom(data, length, da, dc);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param count the number of bytes desired
-     * \param address the client information
-     * \param cond the condition
-     * \return the result string
-     * \throw net::Error on failures
-     */
-    std::string recvfrom(std::size_t count, Address &address, Condition &cond)
-    {
-        std::string result;
-
-        result.resize(count);
-        auto n = recvfrom(const_cast<char *>(result.data()), count, address, cond);
-        result.resize(n);
-
-        return result;
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param count the number of bytes desired
-     * \param address the client information
-     * \return the result string
-     * \throw net::Error on failures
-     */
-    inline std::string recvfrom(std::size_t count, Address &address)
-    {
-        Condition dummy;
-
-        return recvfrom(count, address, dummy);
-    }
-
-    /**
-     * Overloaded function.
-     *
-     * \pre isOpen()
-     * \param count the number of bytes desired
-     * \return the result string
-     * \throw net::Error on failures
-     */
-    inline std::string recvfrom(std::size_t 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);
-
-        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>
-inline 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>
-inline 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>
-inline 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>
-inline 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>
-inline 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>
-inline bool operator>=(const Socket<Address, Protocol> &s1, const Socket<Address, Protocol> &s2)
-{
-    return s1.handle() >= s2.handle();
-}
-
-/**
- * \brief Predefined protocols.
- */
-namespace protocol {
-
-/**
- * \brief Clear TCP implementation.
- * \ingroup net-module-tcp
- *
- * 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
-    {
-    }
-
-    /**
-     * Initiate connection.
-     *
-     * \param sc the socket
-     * \param address the address
-     * \param length the address length
-     * \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.
-     *
-     * User is responsible to wait before the socket is writable, otherwise behavior is undefined.
-     *
-     * \param sc the socket
-     * \param cond the condition
-     */
-    template <typename Address, typename Protocol>
-    void resumeConnect(Socket<Address, Protocol> &sc, Condition &cond)
-    {
-        int error = sc.template get<int>(SOL_SOCKET, SO_ERROR);
-
-#if defined(_WIN32)
-        if (error == WSAEWOULDBLOCK)
-            cond = Condition::Writable;
-        else if (error != 0)
-            throw Error(Error::System, "connect", error);
-#else
-        if (error == EINPROGRESS)
-            cond = Condition::Writable;
-        else if (error != 0)
-            throw Error(Error::System, "connect", error);
-#endif
-    }
-
-    /**
-     * 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 resumeAcept(Socket<Address, Protocol> &, Condition &) const noexcept
-    {
-    }
-
-    /**
-     * Receive some data.
-     *
-     * \param sc the socket
-     * \param data the destination buffer
-     * \param length the buffer length
-     * \param cond the condition
-     * \return the number of byte received
-     */
-    template <typename Address>
-    std::size_t recv(Socket<Address, Tcp> &sc, void *data, std::size_t length, Condition &cond)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbread = ::recv(sc.handle(), (Arg)data, max, 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<std::size_t>(nbread);
-    }
-
-    /**
-     * Send some data.
-     *
-     * \param sc the socket
-     * \param data the data to send
-     * \param length the length
-     * \param cond the condition
-     * \return the number of bytes sent
-     */
-    template <typename Address>
-    std::size_t send(Socket<Address, Tcp> &sc, const void *data, std::size_t length, Condition &cond)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbsent = ::send(sc.handle(), (ConstArg)data, max, 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);
-    }
-};
-
-/**
- * \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
-    {
-    }
-
-    /**
-     * 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>
-    std::size_t recvfrom(Socket<Address, Protocol> &sc, void *data, std::size_t length, sockaddr *address, socklen_t *addrlen, Condition &cond)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbread;
-
-        nbread = ::recvfrom(sc.handle(), (Arg)data, max, 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
-     * \return the number of bytes sent
-     */
-    template <typename Address, typename Protocol>
-    std::size_t sendto(Socket<Address, Protocol> &sc, const void *data, std::size_t length, const sockaddr *address, socklen_t addrlen, Condition &cond)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbsent;
-
-        nbsent = ::sendto(sc.handle(), (ConstArg)data, max, 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)
-
-/**
- * \brief Experimental TLS support.
- * \ingroup net-module-tls
- * \warning This class is highly experimental.
- */
-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))
-    {
-    }
-
-    inline std::string error()
-    {
-        BIO *bio = BIO_new(BIO_s_mem());
-        char *buf = nullptr;
-
-        ERR_print_errors(bio);
-
-        std::size_t length = BIO_get_mem_data (bio, &buf);
-        std::string result(buf, length);
-
-        BIO_free(bio);
-
-        return result;
-    }
-
-    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());
-            }
-        }
-    }
-
-    template <typename Address, typename Protocol>
-    void doConnect(Socket<Address, Protocol> &, Condition &cond)
-    {
-        wrap("connect", cond, [&] () -> int {
-            return SSL_connect(m_ssl.get());
-        });
-    }
-
-    template <typename Address, typename Protocol>
-    void doAccept(Socket<Address, Protocol> &, 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.
-     */
-    inline Tls()
-    {
-#if !defined(NET_NO_SSL_AUTO_INIT)
-        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>
-    void create(Socket<Address, Tls> &sc)
-    {
-        auto method = (m_method == ssl::Tlsv1) ? TLSv1_method() : SSLv23_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(), static_cast<int>(sc.handle()));
-
-        /*
-         * Load certificates, the wrap function requires a condition so just add a dummy value.
-         */
-        Condition dummy;
-
-        if (m_certificate.size() > 0)
-            wrap("SSL_CTX_use_certificate_file", dummy, [&] () -> int {
-                return SSL_CTX_use_certificate_file(m_context.get(), m_certificate.c_str(), SSL_FILETYPE_PEM);
-            });
-        if (m_key.size() > 0)
-            wrap("SSL_CTX_use_PrivateKey_file", dummy, [&] () -> int {
-                return 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");
-    }
-
-    /**
-     * Initiate connection.
-     *
-     * \param sc the socket
-     * \param address the address
-     * \param length the address length
-     * \param cond the condition
-     */
-    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);
-        }
-    }
-
-    /**
-     * Resume the connection.
-     *
-     * \param sc the socket
-     * \param cond the condition to wait
-     */
-    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);
-        }
-
-        // 2. Do SSL connect.
-        if (m_tcpconnected)
-            doConnect(sc, cond);
-    }
-
-    /**
-     * 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
-     * \param cond the condition to wait
-     * \return the new socket
-     */
-    template <typename Address>
-    Socket<Address, Tls> accept(Socket<Address, Tls> &sc, sockaddr *address, socklen_t *length, Condition &cond)
-    {
-        // 1. TCP returns empty client if no pending connection is available.
-        auto client = Tcp::accept(sc, address, length, cond);
-
-        // 2. If a client is available, try initial accept.
-        if (client.isOpen()) {
-            Tls &proto = client.protocol();
-
-            // 2.1. Share the context.
-            proto.m_context = m_context;
-
-            // 2.2. Create new SSL instance.
-            proto.m_ssl = Ssl(SSL_new(m_context.get()), SSL_free);
-
-            SSL_set_fd(proto.m_ssl.get(), static_cast<int>(client.handle()));
-
-            // 2.3. Try accept process on the **new** client.
-            proto.doAccept(client, cond);
-        }
-
-        return client;
-    }
-
-    /**
-     * Resume accept process.
-     *
-     * \param sc the socket
-     * \param cond the condition to wait
-     * \throw net::Error on failures
-     */
-    template <typename Address, typename Protocol>
-    inline void accept(Socket<Address, Protocol> &sc, Condition &cond)
-    {
-        doAccept(sc, cond);
-    }
-
-    /**
-     * Receive some data.
-     *
-     * \param data the destination buffer
-     * \param length the buffer length
-     * \param cond the condition
-     * \return the number of bytes received
-     */
-    template <typename Address>
-    std::size_t recv(Socket<Address, Tls> &, void *data, std::size_t length, Condition &cond)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbread = 0;
-
-        wrap("recv", cond, [&] () -> int {
-            return (nbread = SSL_read(m_ssl.get(), data, max));
-        });
-
-        return static_cast<std::size_t>(nbread < 0 ? 0 : nbread);
-    }
-
-    /**
-     * Send some data.
-     *
-     * \param data the data to send
-     * \param length the length
-     * \param cond the condition
-     * \return the number of bytes sent
-     */
-    template <typename Address>
-    std::size_t send(Socket<Address, Tls> &, const void *data, std::size_t length, Condition &cond)
-    {
-        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
-        int nbsent = 0;
-
-        wrap("send", cond, [&] () -> int {
-            return (nbsent = SSL_write(m_ssl.get(), data, max));
-        });
-
-        return static_cast<std::size_t>(nbsent < 0 ? 0 : nbsent);
-    }
-};
-
-#endif // !NET_NO_SSL
-
-} // !protocol
-
-/**
- * \brief Predefined addresses.
- */
-namespace address {
-
-/**
- * \brief Generic address.
- * \ingroup net-module-addresses
- *
- * This address can store anything that fits into a sockaddr_storage.
- */
-class GenericAddress {
-private:
-    sockaddr_storage m_address;
-    socklen_t m_length{0};
-
-public:
-    /**
-     * Construct a null address.
-     */
-    inline GenericAddress() noexcept
-    {
-        std::memset(&m_address, 0, sizeof (sockaddr_storage));
-    }
-
-    /**
-     * Construct an address.
-     *
-     * \pre address is not null
-     * \pre length <= sizeof (sockaddr_storage)
-     * \param address the address to copy
-     * \param length the address length
-     */
-    inline GenericAddress(const sockaddr *address, socklen_t length) noexcept
+    inline Address(const sockaddr *address, socklen_t length) noexcept
         : m_length(length)
     {
         assert(address);
-        assert(static_cast<unsigned>(length) <= sizeof (sockaddr_storage));
-
-        std::memset(&m_address, 0, sizeof (sockaddr_storage));
-        std::memcpy(&m_address, address, length);
-    }
-
-    /**
-     * Get the address family.
-     *
-     * \return the address family
-     */
-    inline int domain() const noexcept
-    {
-        return m_address.ss_family;
+
+        std::memcpy(&m_storage, address, length);
     }
 
     /**
@@ -2823,19 +1099,41 @@
      *
      * \return the address
      */
-    inline sockaddr *address() noexcept
+    inline const sockaddr *get() const noexcept
     {
-        return reinterpret_cast<sockaddr *>(&m_address);
+        return reinterpret_cast<const sockaddr *>(&m_storage);
+    }
+
+    /**
+     * Overloaded function
+     *
+     * \return the address
+     */
+    inline sockaddr *get() noexcept
+    {
+        return reinterpret_cast<sockaddr *>(&m_storage);
     }
 
     /**
-     * Overloaded function.
+     * Get the underlying address as the given type (e.g sockaddr_in).
      *
-     * \return the address
+     * \return the address reference
      */
-    inline const sockaddr *address() const noexcept
+    template <typename T>
+    inline const T &as() const noexcept
     {
-        return reinterpret_cast<const sockaddr *>(&m_address);
+        return reinterpret_cast<const T &>(m_storage);
+    }
+
+    /**
+     * Overloaded function
+     *
+     * \return the address reference
+     */
+    template <typename T>
+    inline T &as() noexcept
+    {
+        return reinterpret_cast<T &>(m_storage);
     }
 
     /**
@@ -2847,649 +1145,45 @@
     {
         return m_length;
     }
-};
-
-/**
- * Compare two generic addresses.
- *
- * \param a1 the first address
- * \param a2 the second address
- * \return true if they equal
- */
-inline bool operator==(const GenericAddress &a1, const GenericAddress &a2) noexcept
-{
-    return a1.length() == a2.length() && std::memcmp(a1.address(), a2.address(), a1.length()) == 0;
-}
-
-/**
- * Compare two generic addresses.
- *
- * \param a1 the first address
- * \param a2 the second address
- * \return false if they equal
- */
-inline bool operator!=(const GenericAddress &a1, const GenericAddress &a2) noexcept
-{
-    return !(a1 == a2);
-}
-
-/**
- * \brief Generic IP address.
- * \ingroup net-module-addresses
- *
- * You can use this address instead of Ipv4 or Ipv6 if you don't know which address to use at runtime. However,
- * when creating your socket, you will need to define the correct domain.
- */
-class Ip {
-private:
-    union {
-        sockaddr_in6 m_sin6;
-        sockaddr_in m_sin;
-    };
-
-    int m_domain;
-
-public:
-    /**
-     * Create IP address, defaults to IPv4.
-     *
-     * \pre domain must be AF_INET or AF_INET6
-     * \param domain the domain
-     */
-    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));
-    }
 
     /**
-     * Create an IP address on the specific ip address.
+     * Get the address domain.
      *
-     * \pre domain must be AF_INET or AF_INET6
-     * \param ip the address or "*" for any
-     * \param port the port
-     * \param domain the domain
-     * \warning If NET_HAVE_INET_PTON is undefined, host can not be other than "*"
-     * \throw net::Error on failures or if inet_pton is unavailable
-     */
-    inline Ip(const std::string &ip, std::uint16_t port, int domain)
-        : Ip(domain)
-    {
-        if (m_domain == AF_INET)
-            make(ip, port, m_sin);
-        else
-            make(ip, port, m_sin6);
-    }
-
-    /**
-     * Create the IP address from the storage.
-     *
-     * \pre the storage domain must be AF_INET or AF_INET6
-     * \param ss the the storage
-     * \param length the storage length
-     */
-    inline Ip(const sockaddr *ss, socklen_t length) noexcept
-        : Ip(ss->sa_family)
-    {
-        assert(ss->sa_family == AF_INET || ss->sa_family == AF_INET6);
-
-        if (ss->sa_family == AF_INET)
-            std::memcpy(&m_sin, ss, length);
-        else
-            std::memcpy(&m_sin6, ss, length);
-    }
-
-    /**
-     * Get the domain.
-     *
-     * \return AF_INET or AF_INET6
+     * \return the domain
      */
     inline int domain() const noexcept
     {
-        return m_domain;
-    }
-
-    /**
-     * Get the underlying address, may be a sockaddr_in or sockaddr_in6.
-     *
-     * \return the address
-     */
-    inline const sockaddr *address() const noexcept
-    {
-        return m_domain == AF_INET ? reinterpret_cast<const sockaddr *>(&m_sin) : reinterpret_cast<const sockaddr *>(&m_sin6);
-    }
-
-    /**
-     * Get the address length.
-     *
-     * \return the address length
-     */
-    inline socklen_t length() const noexcept
-    {
-        return m_domain == AF_INET ? sizeof (sockaddr_in) : sizeof (sockaddr_in6);
-    }
-
-    /**
-     * Retrieve the port.
-     *
-     * \return the port
-     */
-    inline std::uint16_t port() const noexcept
-    {
-        return m_domain == AF_INET ? ntohs(m_sin.sin_port) : ntohs(m_sin6.sin6_port);
-    }
-
-    /**
-     * Get the ip address.
-     *
-     * \return the ip address
-     * \throw net::Error on errors or if inet_ntop is unavailable
-     */
-    inline std::string ip() const
-    {
-        return m_domain == AF_INET ? ip(m_sin) : ip(m_sin6);
-    }
-
-    /**
-     * Prepare the sockaddr_in structure with the given ip.
-     *
-     * \param ip the ip address
-     * \param port the port
-     * \param sin the Ipv4 address
-     * \throw net::Error if inet_pton is unavailable
-     */
-    static void make(const std::string &ip, 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 (ip == "*")
-            sin.sin_addr.s_addr = INADDR_ANY;
-#if defined(NET_HAVE_INET_PTON)
-        else if (inet_pton(AF_INET, ip.c_str(), &sin.sin_addr) <= 0)
-            throw Error(Error::System, "inet_pton");
-#else
-        else
-            throw Error(Error::System, "inet_pton", std::strerror(ENOSYS));
-#endif
-    }
-
-    /**
-     * Prepare the sockaddr_in structure with the given ip.
-     *
-     * \param ip the ip address
-     * \param port the port
-     * \param sin6 the Ipv6 address
-     * \throw net::Error if inet_pton is unavailable
-     */
-    static void make(const std::string &ip, 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 (ip == "*")
-            sin6.sin6_addr = in6addr_any;
-#if defined(NET_HAVE_INET_PTON)
-        else if (inet_pton(AF_INET6, ip.c_str(), &sin6.sin6_addr) <= 0)
-            throw Error(Error::System, "inet_pton");
-#else
-        else
-            throw Error(Error::System, "inet_pton", std::strerror(ENOSYS));
-#endif
-    }
-
-    /**
-     * Get the underlying ip from the given address.
-     *
-     * \param sin the Ipv4 address
-     * \return the ip address
-     * \throw net::Error if inet_ntop is unavailable
-     */
-    static std::string ip(const sockaddr_in &sin)
-    {
-#if !defined(NET_NO_AUTO_INIT)
-        net::init();
-#endif
-
-#if !defined(NET_HAVE_INET_NTOP)
-        (void)sin;
-
-        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
-    }
-
-    /**
-     * Get the underlying ip from the given address.
-     *
-     * \param sin6 the Ipv6 address
-     * \return the ip address
-     * \throw net::Error if inet_ntop is unavailable
-     */
-    static std::string ip(const sockaddr_in6 &sin6)
-    {
-#if !defined(NET_NO_AUTO_INIT)
-        net::init();
-#endif
-
-#if !defined(NET_HAVE_INET_NTOP)
-        (void)sin6;
-
-        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
-    }
-
-    /**
-     * Resolve an hostname.
-     *
-     * This function wraps getaddrinfo and returns the first result.
-     *
-     * \param host the hostname
-     * \param service the service name (port or name)
-     * \param domain the domain (e.g. AF_INET)
-     * \param type the socket type (e.g. SOCK_STREAM)
-     * \return the resolved address
-     * \throw net::Error on failures
-     */
-    static Ip resolve(const std::string &host, const std::string &service, int domain = AF_INET, int type = SOCK_STREAM)
-    {
-        assert(domain == AF_INET || domain == AF_INET6);
-#if !defined(NET_NO_AUTO_INIT)
-        net::init();
-#endif
-
-        struct addrinfo hints, *res;
-
-        std::memset(&hints, 0, sizeof (struct addrinfo));
-        hints.ai_family = domain;
-        hints.ai_socktype = type;
-
-        int e = getaddrinfo(host.c_str(), service.c_str(), &hints, &res);
-
-        if (e != 0)
-            throw Error(Error::System, "getaddrinfo", gai_strerror(e));
-
-        Ip ip(res->ai_addr, res->ai_addrlen);
-
-        freeaddrinfo(res);
-
-        return ip;
+        return m_storage.ss_family;
     }
 };
 
 /**
- * \brief Ipv4 only address.
- * \ingroup net-module-addresses
- */
-class Ipv4 {
-private:
-    sockaddr_in m_sin;
-
-public:
-    /**
-     * Create an Ipv4 address.
-     */
-    inline Ipv4() noexcept
-    {
-        std::memset(&m_sin, 0, sizeof (sockaddr_in));
-    }
-
-    /**
-     * Create an Ipv4 address on the specific ip address.
-     *
-     * \param ip the address or "*" for any
-     * \param port the port
-     * \warning If NET_HAVE_INET_PTON is undefined, host can not be other than "*"
-     * \throw net::Error on failures or if inet_pton is unavailable
-     */
-    inline Ipv4(const std::string &ip, std::uint16_t port)
-        : Ipv4()
-    {
-        Ip::make(ip, port, m_sin);
-    }
-
-    /**
-     * Create the IP address from the storage.
-     *
-     * \pre the storage domain must be AF_INET
-     * \param ss the the storage
-     * \param length the storage length
-     */
-    inline Ipv4(const sockaddr *ss, socklen_t length) noexcept
-    {
-        assert(ss->sa_family == AF_INET);
-
-        std::memcpy(&m_sin, ss, length);
-    }
-
-    /**
-     * Get the domain.
-     *
-     * \return AF_INET
-     */
-    inline int domain() const noexcept
-    {
-        return AF_INET;
-    }
-
-    /**
-     * Get the underlying address.
-     *
-     * \return the address
-     */
-    inline const sockaddr *address() const noexcept
-    {
-        return reinterpret_cast<const sockaddr *>(&m_sin);
-    }
-
-    /**
-     * Get the address length.
-     *
-     * \return the size of sockaddr_in
-     */
-    inline socklen_t length() const noexcept
-    {
-        return sizeof (sockaddr_in);
-    }
-
-    /**
-     * Get the port.
-     *
-     * \return the port
-     */
-    inline std::uint16_t port() const noexcept
-    {
-        return ntohs(m_sin.sin_port);
-    }
-
-    /**
-     * Get the ip address.
-     *
-     * \return the ip address
-     * \throw net::Error on errors or if inet_ntop is unavailable
-     */
-    inline std::string ip() const
-    {
-        return Ip::ip(m_sin);
-    }
-
-    /**
-     * Same as Ip::resolve with AF_INET as domain.
-     *
-     * \param host the hostname
-     * \param service the service name (port or name)
-     * \param type the socket type (e.g. SOCK_STREAM)
-     * \return the resolved address
-     * \throw net::Error on failures
-     */
-    static Ipv4 resolve(const std::string &host, const std::string &service, int type = SOCK_STREAM)
-    {
-        Ip result = Ip::resolve(host, service, AF_INET, type);
-
-        return Ipv4(result.address(), result.length());
-    }
-};
-
-/**
- * \brief Ipv4 only address.
- * \ingroup net-module-addresses
- */
-class Ipv6 {
-private:
-    sockaddr_in6 m_sin6;
-
-public:
-    /**
-     * Create an Ipv6 address.
-     */
-    inline Ipv6() noexcept
-    {
-        std::memset(&m_sin6, 0, sizeof (sockaddr_in6));
-    }
-
-    /**
-     * Create an Ipv6 address on the specific ip address.
-     *
-     * \param ip the address or "*" for any
-     * \param port the port
-     * \warning If NET_HAVE_INET_PTON is undefined, host can not be other than "*"
-     * \throw net::Error on failures or if inet_pton is unavailable
-     */
-    inline Ipv6(const std::string &ip, std::uint16_t port)
-        : Ipv6()
-    {
-        Ip::make(ip, port, m_sin6);
-    }
-
-    /**
-     * Create the IP address from the storage.
-     *
-     * \pre the storage domain must be AF_INET6
-     * \param ss the the storage
-     * \param length the storage length
-     */
-    inline Ipv6(const sockaddr *ss, socklen_t length) noexcept
-    {
-        assert(ss->sa_family == AF_INET6);
-
-        std::memcpy(&m_sin6, ss, length);
-    }
-
-    /**
-     * Get the domain.
-     *
-     * \return AF_INET6
-     */
-    inline int domain() const noexcept
-    {
-        return AF_INET6;
-    }
-
-    /**
-     * Get the underlying address.
-     *
-     * \return the address
-     */
-    inline const sockaddr *address() const noexcept
-    {
-        return reinterpret_cast<const sockaddr *>(&m_sin6);
-    }
-
-    /**
-     * Get the address length.
-     *
-     * \return the size of sockaddr_in
-     */
-    inline socklen_t length() const noexcept
-    {
-        return sizeof (sockaddr_in6);
-    }
-
-    /**
-     * Get the port.
-     *
-     * \return the port
-     */
-    inline std::uint16_t port() const noexcept
-    {
-        return ntohs(m_sin6.sin6_port);
-    }
-
-    /**
-     * Get the ip address.
-     *
-     * \return the ip address
-     * \throw net::Error on errors or if inet_ntop is unavailable
-     */
-    inline std::string ip() const
-    {
-        return Ip::ip(m_sin6);
-    }
-
-    /**
-     * Same as Ip::resolve with AF_INET6 as domain.
-     *
-     * \param host the hostname
-     * \param service the service name (port or name)
-     * \param type the socket type (e.g. SOCK_STREAM)
-     * \return the resolved address
-     * \throw net::Error on failures
-     */
-    static Ipv6 resolve(const std::string &host, const std::string &service, int type = SOCK_STREAM)
-    {
-        Ip result = Ip::resolve(host, service, AF_INET6, type);
-
-        return Ipv6(result.address(), result.length());
-    }
-};
-
-#if !defined(_WIN32)
-
-/**
- * \brief unix family sockets
- * \ingroup net-module-addresses
- *
- * 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 *ss, socklen_t length) noexcept
-    {
-        assert(ss->sa_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
-
-/**
  * \brief Address iterator.
  * \ingroup net-module-addresses
  * \see resolve
  *
  * This iterator can be used to try to connect to an host.
  *
- * When you use net::resolve with unspecified domain or socket type, the function may retrieve several different addresses that you can
- * iterate over to try to connect to.
+ * When you use resolve with unspecified domain or socket type, the function may
+ * retrieve several different addresses that you can iterate over to try to
+ * connect to.
  *
  * Example:
  *
  * ````cpp
- * net::SocketTcpIp sc;
- * net::AddressIterator end, it = net::resolve("hostname.test", "80");
+ * SocketTcp sc;
+ * AddressIterator end, it = resolve("hostname.test", "80");
  *
  * while (!connected_condition && it != end)
  *   sc.connect(it->address(), it->length());
  * ````
  *
- * When an iterator equals to a default constructed iterator, it is considered not dereferenceable.
+ * When an iterator equals to a default constructed iterator, it is considered
+ * not dereferenceable.
  */
-class AddressIterator : public std::iterator<std::forward_iterator_tag, GenericAddress> {
+class AddressIterator : public std::iterator<std::forward_iterator_tag, Address> {
 private:
-    std::vector<GenericAddress> m_addresses;
+    std::vector<Address> m_addresses;
     std::size_t m_index{0};
 
 public:
@@ -3507,7 +1201,7 @@
      * \param addresses the addresses
      * \param index the first index
      */
-    inline AddressIterator(std::vector<GenericAddress> addresses, std::size_t index = 0) noexcept
+    inline AddressIterator(std::vector<Address> addresses, std::size_t index = 0) noexcept
         : m_addresses(std::move(addresses))
         , m_index(index)
     {
@@ -3520,7 +1214,7 @@
      * \pre this is dereferenceable
      * \return the generic address
      */
-    inline const GenericAddress &operator*() const noexcept
+    inline const Address &operator*() const noexcept
     {
         assert(m_index <= m_addresses.size());
 
@@ -3533,7 +1227,7 @@
      * \pre this is dereferenceable
      * \return the generic address
      */
-    inline GenericAddress &operator*() noexcept
+    inline Address &operator*() noexcept
     {
         assert(m_index <= m_addresses.size());
 
@@ -3546,7 +1240,7 @@
      * \pre this is dereferenceable
      * \return the generic address
      */
-    inline const GenericAddress *operator->() const noexcept
+    inline const Address *operator->() const noexcept
     {
         assert(m_index <= m_addresses.size());
 
@@ -3559,7 +1253,7 @@
      * \pre this is dereferenceable
      * \return the generic address
      */
-    inline GenericAddress *operator->() noexcept
+    inline Address *operator->() noexcept
     {
         assert(m_index <= m_addresses.size());
 
@@ -3628,7 +1322,1201 @@
     return !(i1 == i2);
 }
 
-} // !address
+/**
+ * Compare two generic addresses.
+ *
+ * \param a1 the first address
+ * \param a2 the second address
+ * \return true if they equal
+ */
+inline bool operator==(const Address &a1, const Address &a2) noexcept
+{
+    return a1.length() == a2.length() && std::memcmp(a1.get(), a2.get(), a1.length()) == 0;
+}
+
+/**
+ * Compare two generic addresses.
+ *
+ * \param a1 the first address
+ * \param a2 the second address
+ * \return false if they equal
+ */
+inline bool operator!=(const Address &a1, const Address &a2) noexcept
+{
+    return !(a1 == a2);
+}
+
+/**
+ * \brief Base socket class.
+ */
+class Socket {
+protected:
+    /**
+     * The native handle.
+     */
+    Handle m_handle{Invalid};
+
+public:
+    /**
+     * Create a socket handle.
+     *
+     * This is the primary function and the only one that creates the socket
+     * handle, all other constructors are just overloaded functions.
+     *
+     * \param domain the domain AF_*
+     * \param type the type SOCK_*
+     * \param protocol the protocol
+     * \throw Error on errors
+     */
+    Socket(int domain, int type, int protocol)
+    {
+#if !defined(NET_NO_AUTO_INIT)
+        init();
+#endif
+        m_handle = ::socket(domain, type, protocol);
+
+        if (m_handle == Invalid)
+            throw Error();
+    }
+
+    /**
+     * Create the socket with an already defined handle and its protocol.
+     *
+     * \param handle the handle
+     */
+    explicit inline Socket(Handle handle) noexcept
+        : m_handle(handle)
+    {
+    }
+
+    /**
+     * Create an invalid socket. Can be used when you cannot instanciate the
+     * socket immediately.
+     */
+    explicit inline Socket(std::nullptr_t) noexcept
+        : m_handle(Invalid)
+    {
+    }
+
+    /**
+     * Copy constructor deleted.
+     */
+    Socket(const Socket &) = delete;
+
+    /**
+     * Transfer ownership from other to this.
+     *
+     * \param other the other socket
+     */
+    inline Socket(Socket &&other) noexcept
+        : m_handle(other.m_handle)
+    {
+        other.m_handle = Invalid;
+    }
+
+    /**
+     * Default destructor.
+     */
+    virtual ~Socket()
+    {
+        close();
+    }
+
+    /**
+     * Tells if the socket is not invalid.
+     *
+     * \return true if not invalid
+     */
+    inline bool isOpen() const noexcept
+    {
+        return m_handle != Invalid;
+    }
+
+    /**
+     * Set an option for the socket. Wrapper of setsockopt(2).
+     *
+     * \pre isOpen()
+     * \param level the setting level
+     * \param name the name
+     * \param arg the value
+     * \throw Error on errors
+     */
+    template <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();
+    }
+
+    /**
+     * Object-oriented option setter.
+     *
+     * The object must have `set(Socket &) const`.
+     *
+     * \pre isOpen()
+     * \param option the option
+     * \throw 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
+     * \return the value
+     * \throw 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();
+
+        std::memcpy(&result, &desired, size);
+
+        return result;
+    }
+
+    /**
+     * Object-oriented option getter.
+     *
+     * The object must have `T get(Socket &) const`, T can be any type and it is
+     * the value returned from this function.
+     *
+     * \pre isOpen()
+     * \return the same value as get() in the option
+     * \throw Error on errors
+     */
+    template <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 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();
+    }
+
+    /**
+     * Overload that takes an address.
+     *
+     * \pre isOpen()
+     * \param address the address
+     * \throw Error on errors
+     */
+    inline void bind(const Address &address)
+    {
+        assert(m_handle != Invalid);
+
+        bind(address.get(), address.length());
+    }
+
+    /**
+     * Listen for pending connection.
+     *
+     * \pre isOpen()
+     * \param max the maximum number
+     * \throw Error on errors
+     */
+    inline void listen(int max = 128)
+    {
+        assert(m_handle != Invalid);
+
+        if (::listen(m_handle, max) == Failure)
+            throw Error();
+    }
+
+    /**
+     * Get the local name. This is a wrapper of getsockname().
+     *
+     * \pre isOpen()
+     * \return the address
+     * \throw Error on failures
+     */
+    Address getsockname() 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();
+
+        return Address(reinterpret_cast<sockaddr *>(&ss), length);
+    }
+
+    /**
+     * Get connected address. This is a wrapper for getpeername().
+     *
+     * \pre isOpen()
+     * \return the address
+     * \throw Error on failures
+     */
+    Address getpeername() const
+    {
+        assert(m_handle != Invalid);
+
+        sockaddr_storage ss;
+        socklen_t length = sizeof (sockaddr_storage);
+
+        if (::getpeername(m_handle, reinterpret_cast<sockaddr *>(&ss), &length) == Failure)
+            throw Error();
+
+        return Address(reinterpret_cast<sockaddr *>(&ss), length);
+    }
+
+    /**
+     * 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;
+        other.m_handle = Invalid;
+
+        return *this;
+    }
+};
+
+/**
+ * Compare two sockets.
+ *
+ * \param s1 the first socket
+ * \param s2 the second socket
+ * \return true if they equals
+ */
+inline bool operator==(const Socket &s1, const Socket &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
+ */
+inline bool operator!=(const Socket &s1, const Socket &s2)
+{
+    return s1.handle() != s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * \param s1 the first socket
+ * \param s2 the second socket
+ * \return true if s1 < s2
+ */
+inline bool operator<(const Socket &s1, const Socket &s2)
+{
+    return s1.handle() < s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * \param s1 the first socket
+ * \param s2 the second socket
+ * \return true if s1 > s2
+ */
+inline bool operator>(const Socket &s1, const Socket &s2)
+{
+    return s1.handle() > s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * \param s1 the first socket
+ * \param s2 the second socket
+ * \return true if s1 <= s2
+ */
+inline bool operator<=(const Socket &s1, const Socket &s2)
+{
+    return s1.handle() <= s2.handle();
+}
+
+/**
+ * Compare two sockets.
+ *
+ * \param s1 the first socket
+ * \param s2 the second socket
+ * \return true if s1 >= s2
+ */
+inline bool operator>=(const Socket &s1, const Socket &s2)
+{
+    return s1.handle() >= s2.handle();
+}
+
+/**
+ * \brief Clear TCP implementation.
+ * \ingroup net-module-tcp
+ *
+ * This is the basic TCP protocol that implements recv, send, connect and accept
+ * as wrappers of the usual C functions.
+ */
+class TcpSocket : public Socket {
+public:
+    /**
+     * Inherited constructors.
+     */
+    using Socket::Socket;
+
+    /**
+     * Construct a TCP socket.
+     *
+     * \param domain the domain
+     * \param protocol the protocol
+     * \throw Error on errors
+     */
+    inline TcpSocket(int domain, int protocol)
+        : Socket(domain, SOCK_STREAM, protocol)
+    {
+    }
+
+    /**
+     * Get the type of the socket.
+     *
+     * \return the type
+     */
+    inline int type() const noexcept
+    {
+        return SOCK_STREAM;
+    }
+
+    /**
+     * Initiate connection.
+     *
+     * \param address the address
+     * \param length the address length
+     * \throw WouldBlockError if the socket is marked non-blocking and
+     * connection cannot be established immediately
+     * \throw Error on other errors
+     */
+    void connect(const sockaddr *address, socklen_t length)
+    {
+        if (::connect(this->m_handle, address, length) == Failure) {
+#if defined(_WIN32)
+            int error = WSAGetLastError();
+
+            if (error == WSAEWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error(error);
+#else
+            if (errno == EINPROGRESS)
+                throw WouldBlockError();
+            else
+                throw Error();
+#endif
+        }
+    }
+
+    /**
+     * Overloaded function.
+     *
+     * \param address the address
+     * \throw WouldBlockError if the socket is marked non-blocking and
+     * connection cannot be established immediately
+     * \throw Error on other errors
+     */
+    void connect(const Address &address)
+    {
+        connect(address.get(), address.length());
+    }
+
+    /**
+     * Accept a new client.
+     *
+     * If there are no pending connection, an invalid socket is returned.
+     *
+     * \return the new socket
+     * \throw WouldBlockError if the socket is marked non-blocking and no
+     * connection are available
+     * \throw Error on other errors
+     */
+    TcpSocket accept()
+    {
+        Handle handle = ::accept(this->m_handle, nullptr, 0);
+
+        if (handle == Invalid) {
+#if defined(_WIN32)
+            int error = WSAGetLastError();
+
+            if (error == WSAEWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error(error);
+#else
+            if (errno == EWOULDBLOCK || errno == EAGAIN)
+                throw WouldBlockError();
+            else
+                throw Error();
+#endif
+        }
+
+        return TcpSocket(handle);
+    }
+
+    /**
+     * Receive some data.
+     *
+     * \param data the destination buffer
+     * \param length the buffer length
+     * \return the number of bytes received
+     */
+    unsigned recv(void *data, unsigned length)
+    {
+        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
+        int nbread = ::recv(this->m_handle, (Arg)data, max, 0);
+
+        if (nbread == Failure) {
+#if defined(_WIN32)
+            int error = WSAGetLastError();
+
+            if (error == WSAEWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error(error);
+#else
+            if (errno == EAGAIN || errno == EWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error();
+#endif
+        }
+
+        return static_cast<unsigned>(nbread);
+    }
+
+    /**
+     * Send some data.
+     *
+     * \param data the data to send
+     * \param length the length
+     * \return the number of bytes sent
+     */
+    unsigned send(const void *data, unsigned length)
+    {
+        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
+        int nbsent = ::send(this->m_handle, (ConstArg)data, max, 0);
+
+        if (nbsent == Failure) {
+#if defined(_WIN32)
+            int error = WSAGetLastError();
+
+            if (error == WSAEWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error();
+#else
+            if (errno == EAGAIN || errno == EWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error();
+#endif
+        }
+
+        return static_cast<unsigned>(nbsent);
+    }
+};
+
+/**
+ * \brief Clear UDP type.
+ *
+ * This class is the basic implementation of UDP sockets.
+ */
+class UdpSocket : public Socket {
+public:
+    /**
+     * Inherited constructors.
+     */
+    using Socket::Socket;
+
+    /**
+     * Construct a TCP socket.
+     *
+     * \param domain the domain
+     * \param protocol the protocol
+     * \throw Error on errors
+     */
+    inline UdpSocket(int domain, int protocol)
+        : Socket(domain, SOCK_DGRAM, protocol)
+    {
+    }
+
+    /**
+     * Get the type of the socket.
+     *
+     * \return the type
+     */
+    inline int type() const noexcept
+    {
+        return SOCK_DGRAM;
+    }
+
+    /**
+     * Receive some data.
+     *
+     * \param data the data
+     * \param length the length
+     * \param address the source address
+     * \param addrlen the source address in/out length
+     * \return the number of bytes received
+     * \throw WouldBlockError if the socket is marked non-blocking and the
+     * operation would block
+     * \throw Error on other errors
+     */
+    unsigned recvfrom(void *data, unsigned length, sockaddr *address, socklen_t *addrlen)
+    {
+        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
+        int nbread = ::recvfrom(this->m_handle, (Arg)data, max, 0, address, addrlen);
+
+        if (nbread == Failure) {
+#if defined(_WIN32)
+            int error = WSAGetLastError();
+
+            if (error == EWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error(error);
+#else
+            if (errno == EAGAIN || errno == EWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error();
+#endif
+        }
+
+        return static_cast<unsigned>(nbread);
+    }
+
+    /**
+     * Overloaded function.
+     *
+     * \param data the data
+     * \param length the length
+     * \param source the source information (optional)
+     * \throw WouldBlockError if the socket is marked non-blocking and the
+     * operation would block
+     * \throw Error on other errors
+     */
+    inline unsigned recvfrom(void *data, unsigned length, Address *source = nullptr)
+    {
+        sockaddr_storage st;
+        socklen_t socklen = sizeof (sockaddr_storage);
+
+        auto nr = recvfrom(data, length, reinterpret_cast<sockaddr *>(&st), &socklen);
+
+        if (source)
+            *source = Address(reinterpret_cast<const sockaddr *>(&st), socklen);
+
+        return nr;
+    }
+
+    /**
+     * Send some data.
+     *
+     * \param data the data to send
+     * \param length the data length
+     * \param address the destination address
+     * \param addrlen the destination address length
+     * \return the number of bytes sent
+     * \throw WouldBlockError if the socket is marked non-blocking and the
+     * operation would block
+     * \throw Error on other errors
+     */
+    unsigned sendto(const void *data, unsigned length, const sockaddr *address, socklen_t addrlen)
+    {
+        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
+        int nbsent = ::sendto(this->m_handle, (ConstArg)data, max, 0, address, addrlen);
+
+        if (nbsent == Failure) {
+#if defined(_WIN32)
+            int error = WSAGetLastError();
+
+            if (error == EWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error(error);
+#else
+            if (errno == EAGAIN || errno == EWOULDBLOCK)
+                throw WouldBlockError();
+            else
+                throw Error();
+#endif
+        }
+
+        return static_cast<unsigned>(nbsent);
+    }
+
+    /**
+     * Overloaded function
+     *
+     * \param data the data to send
+     * \param length the data length
+     * \param address the destination address
+     * \return the number of bytes sent
+     * \throw WouldBlockError if the socket is marked non-blocking and the
+     * operation would block
+     * \throw Error on other errors
+     */
+    inline unsigned sendto(const void *data, unsigned length, const Address &address)
+    {
+        return sendto(data, length, address.get(), address.length());
+    }
+};
+
+#if !defined(NET_NO_SSL)
+
+/**
+ * \brief Experimental TLS support.
+ * \ingroup net-module-tls
+ * \warning This class is highly experimental.
+ */
+class TlsSocket : public Socket {
+public:
+    /**
+     * \brief SSL connection mode.
+     */
+    enum Mode {
+        Server,         //!< Use Server when you accept a socket server side,
+        Client          //!< Use Client when you connect to a server.
+    };
+
+private:
+    using Context = std::shared_ptr<SSL_CTX>;
+    using Ssl = std::unique_ptr<SSL, void (*)(SSL *)>;
+
+    // Determine if we created a TlsSocket from a temporary or a lvalue.
+    bool m_mustclose{false};
+
+    Context m_context;
+    Ssl m_ssl{nullptr, nullptr};
+
+    inline std::string error()
+    {
+        BIO *bio = BIO_new(BIO_s_mem());
+        char *buf = nullptr;
+
+        ERR_print_errors(bio);
+
+        std::size_t length = BIO_get_mem_data (bio, &buf);
+        std::string result(buf, length);
+
+        BIO_free(bio);
+
+        return result;
+    }
+
+    template <typename Function>
+    void wrap(Function &&function)
+    {
+        auto ret = function();
+
+        if (ret <= 0) {
+            int no = SSL_get_error(m_ssl.get(), ret);
+
+            switch (no) {
+            case SSL_ERROR_WANT_READ:
+                throw WantReadError();
+            case SSL_ERROR_WANT_WRITE:
+                throw WantWriteError();
+            default:
+                throw Error(error());
+            }
+        }
+    }
+
+    void create(Mode mode, const SSL_METHOD *method)
+    {
+#if !defined(NET_NO_SSL_AUTO_INIT)
+        ssl::init();
+#endif
+        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(), this->m_handle);
+
+        if (mode == Server)
+            SSL_set_accept_state(m_ssl.get());
+        else
+            SSL_set_connect_state(m_ssl.get());
+    }
+
+public:
+    /**
+     * Create a socket around an existing one.
+     *
+     * The original socket is moved to this instance and must not be used
+     * anymore.
+     *
+     * \param sock the TCP socket
+     * \param mode the mode
+     * \param method the method
+     */
+    TlsSocket(TcpSocket &&sock, Mode mode = Server, const SSL_METHOD *method = TLSv1_method())
+        : Socket(std::move(sock))
+        , m_mustclose(true)
+    {
+        create(mode, method);
+    }
+
+    /**
+     * Wrap a socket around an existing one without taking ownership.
+     *
+     * The original socket must still exist until this TlsSocket is closed.
+     *
+     * \param sock the TCP socket
+     * \param mode the mode
+     * \param method the method
+     */
+    TlsSocket(TcpSocket &sock, Mode mode = Server, const SSL_METHOD *method = TLSv1_method())
+        : Socket(sock.handle())
+    {
+        create(mode, method);
+    }
+
+    /**
+     * Destroy the socket if owned.
+     */
+    ~TlsSocket()
+    {
+        /**
+         * If the socket has been created from a rvalue this class owns the
+         * socket and will close it in the parent destructor.
+         *
+         * Otherwise, when created from a lvalue, mark this socket as invalid
+         * to avoid double close'ing it as two sockets points to the same
+         * descriptor.
+         */
+        if (!m_mustclose)
+            m_handle = Invalid;
+    }
+
+    /**
+     * Get the type of socket.
+     *
+     * \return the type
+     */
+    inline int type() const noexcept
+    {
+        return SOCK_STREAM;
+    }
+
+    /**
+     * Use the specified private key file.
+     *
+     * \param file the path to the private key
+     * \param type the type of file
+     */
+    inline void setPrivateKey(std::string file, int type = SSL_FILETYPE_PEM)
+    {
+        if (SSL_use_PrivateKey_file(m_ssl.get(), file.c_str(), type) != 1)
+            throw Error(error());
+    }
+
+    /**
+     * Use the specified certificate file.
+     *
+     * \param file the path to the file
+     * \param type the type of file
+     */
+    inline void setCertificate(std::string file, int type = SSL_FILETYPE_PEM)
+    {
+        if (SSL_use_certificate_file(m_ssl.get(), file.c_str(), type) != 1)
+            throw Error(error());
+    }
+
+    /**
+     * Do handshake, needed in some case when you have non blocking sockets.
+     */
+    void handshake()
+    {
+        wrap([&] () -> int {
+            return SSL_do_handshake(m_ssl.get());
+        });
+    }
+
+    /**
+     * Receive some data.
+     *
+     * \param data the destination buffer
+     * \param length the buffer length
+     * \return the number of bytes received
+     */
+    unsigned recv(void *data, unsigned length)
+    {
+        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
+        int nbread = 0;
+
+        wrap([&] () -> int {
+            return (nbread = SSL_read(m_ssl.get(), data, max));
+        });
+
+        return static_cast<unsigned>(nbread < 0 ? 0 : nbread);
+    }
+
+    /**
+     * Send some data.
+     *
+     * \param data the data to send
+     * \param length the length
+     * \return the number of bytes sent
+     */
+    unsigned send(const void *data, unsigned length)
+    {
+        int max = length > INT_MAX ? INT_MAX : static_cast<int>(length);
+        int nbsent = 0;
+
+        wrap([&] () -> int {
+            return (nbsent = SSL_write(m_ssl.get(), data, max));
+        });
+
+        return static_cast<unsigned>(nbsent < 0 ? 0 : nbsent);
+    }
+};
+
+#endif // !NET_NO_SSL
+
+/**
+ * \brief IPv4 functions.
+ */
+namespace ipv4 {
+
+/**
+ * Create an address to bind on any.
+ *
+ * \param port the port
+ * \return the address
+ */
+inline Address any(std::uint16_t port)
+{
+    sockaddr_in sin;
+    socklen_t length = sizeof (sockaddr_in);
+
+    std::memset(&sin, 0, sizeof (sockaddr_in));
+    sin.sin_family = AF_INET;
+    sin.sin_addr.s_addr = INADDR_ANY;
+    sin.sin_port = htons(port);
+
+    return Address(reinterpret_cast<const sockaddr *>(&sin), length);
+}
+
+/**
+ * Create an address from a IPv4 string.
+ *
+ * \param ip the ip address
+ * \param port the port
+ * \return the address
+ * \throw Error if inet_pton is unavailable
+ */
+inline Address pton(const std::string &ip, std::uint16_t port)
+{
+#if defined(NET_HAVE_INET_PTON)
+#if !defined(NET_NO_AUTO_INIT)
+    init();
+#endif
+
+    sockaddr_in sin;
+    socklen_t length = sizeof (sockaddr_in);
+
+    std::memset(&sin, 0, sizeof (sockaddr_in));
+    sin.sin_family = AF_INET;
+    sin.sin_port = htons(port);
+
+    if (inet_pton(AF_INET, ip.c_str(), &sin.sin_addr) <= 0)
+        throw Error();
+
+    return Address(reinterpret_cast<const sockaddr *>(&sin), length);
+#else
+    (void)ip;
+    (void)port;
+
+    throw Error(std::strerror(ENOSYS));
+#endif
+}
+
+/**
+ * Get the underlying ip from the given address.
+ *
+ * \pre address.domain() == AF_INET
+ * \param address the IPv6 address
+ * \return the ip address
+ * \throw Error if inet_ntop is unavailable
+ */
+inline std::string ntop(const Address &address)
+{
+    assert(address.domain() == AF_INET);
+
+#if !defined(NET_NO_AUTO_INIT)
+    init();
+#endif
+
+#if defined(NET_HAVE_INET_NTOP)
+    char result[INET_ADDRSTRLEN + 1];
+
+    std::memset(result, 0, sizeof (result));
+
+    if (!inet_ntop(AF_INET, const_cast<in_addr *>(&address.as<sockaddr_in>().sin_addr), result, sizeof (result)))
+        throw Error();
+
+    return result;
+#else
+    (void)address;
+
+    throw Error(std::strerror(ENOSYS));
+#endif
+}
+
+/**
+ * Get the port from the IPv4 address.
+ *
+ * \pre address.domain() == AF_INET4
+ * \param address the address
+ * \return the port
+ */
+inline std::uint16_t port(const Address &address) noexcept
+{
+    assert(address.domain() == AF_INET);
+
+    return ntohs(address.as<sockaddr_in>().sin_port);
+}
+
+} // !ipv4
+
+/**
+ * \brief IPv6 functions.
+ */
+namespace ipv6 {
+
+/**
+ * Create an address to bind on any.
+ *
+ * \param port the port
+ * \return the address
+ */
+inline Address any(std::uint16_t port)
+{
+    sockaddr_in6 sin6;
+    socklen_t length = sizeof (sockaddr_in6);
+
+    std::memset(&sin6, 0, sizeof (sockaddr_in6));
+    sin6.sin6_family = AF_INET6;
+    sin6.sin6_addr = in6addr_any;
+    sin6.sin6_port = htons(port);
+
+    return Address(reinterpret_cast<const sockaddr *>(&sin6), length);
+}
+
+/**
+ * Create an address from a IPv4 string.
+ *
+ * \param ip the ip address
+ * \param port the port
+ * \return the address
+ * \throw Error if inet_pton is unavailable
+ */
+inline Address pton(const std::string &ip, std::uint16_t port)
+{
+#if defined(NET_HAVE_INET_PTON)
+#if !defined(NET_NO_AUTO_INIT)
+    init();
+#endif
+
+    sockaddr_in6 sin6;
+    socklen_t length = sizeof (sockaddr_in6);
+
+    std::memset(&sin6, 0, sizeof (sockaddr_in6));
+    sin6.sin6_family = AF_INET6;
+    sin6.sin6_port = htons(port);
+
+    if (inet_pton(AF_INET6, ip.c_str(), &sin6.sin6_addr) <= 0)
+        throw Error();
+
+    return Address(reinterpret_cast<const sockaddr *>(&sin6), length);
+#else
+    (void)ip;
+    (void)port;
+
+    throw Error(std::strerror(ENOSYS));
+#endif
+}
+
+/**
+ * Get the underlying ip from the given address.
+ *
+ * \pre address.domain() == AF_INET6
+ * \param address the IPv6 address
+ * \return the ip address
+ * \throw Error if inet_ntop is unavailable
+ */
+inline std::string ntop(const Address &address)
+{
+    assert(address.domain() == AF_INET6);
+
+#if defined(NET_HAVE_INET_NTOP)
+#if !defined(NET_NO_AUTO_INIT)
+    init();
+#endif
+
+    char ret[INET6_ADDRSTRLEN];
+
+    std::memset(ret, 0, sizeof (ret));
+
+    if (!inet_ntop(AF_INET6, const_cast<in6_addr *>(&address.as<sockaddr_in6>().sin6_addr), ret, sizeof (ret)))
+        throw Error();
+
+    return ret;
+#else
+    (void)address;
+
+    throw Error(std::strerror(ENOSYS));
+#endif
+}
+
+/**
+ * Get the port from the IPv6 address.
+ *
+ * \pre address.domain() == AF_INET6
+ * \param address the address
+ * \return the port
+ */
+inline std::uint16_t port(const Address &address) noexcept
+{
+    assert(address.domain() == AF_INET6);
+
+    return ntohs(address.as<sockaddr_in6>().sin6_port);
+}
+
+} // !ipv6
+
+#if !defined(_WIN32)
+
+/**
+ * \brief Unix domain functions.
+ */
+namespace local {
+
+/**
+ * Construct an address to a path.
+ *
+ * \pre !path.empty()
+ * \param path the path
+ * \param rm remove the file before (default: false)
+ */
+inline Address create(const std::string &path, bool rm = false) noexcept
+{
+    assert(!path.empty());
+
+    // Silently remove the file even if it fails.
+    if (rm)
+        remove(path.c_str());
+
+    sockaddr_un sun;
+    socklen_t length;
+
+    std::memset(sun.sun_path, 0, sizeof (sun.sun_path));
+    std::strncpy(sun.sun_path, path.c_str(), sizeof (sun.sun_path) - 1);
+
+    sun.sun_family = AF_LOCAL;
+
+#if defined(NET_HAVE_SUN_LEN)
+    length = SUN_LEN(&sun);
+#else
+    length = sizeof (sun);
+#endif
+
+    return Address(reinterpret_cast<const sockaddr *>(&sun), length); 
+}
+
+/**
+ * Get the path from the address.
+ *
+ * \pre address.domain() == AF_LOCAL
+ * \param address the local address
+ * \return the path to the socket file
+ */
+inline std::string path(const Address &address)
+{
+    assert(address.domain() == AF_LOCAL);
+
+    return reinterpret_cast<const sockaddr_un *>(address.get())->sun_path;
+}
+
+} // !local
+
+#endif // !_WIN32
 
 /**
  * \brief Predefined options.
@@ -3638,7 +2526,8 @@
 /**
  * \ingroup net-module-options
  * \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.
+ * \warning On Windows, it's not possible to check if the socket is blocking or
+ * not.
  */
 class SockBlockMode {
 private:
@@ -3663,8 +2552,7 @@
      * \param sc the socket
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    void set(Socket<Address, Protocol> &sc) const
+    void set(Socket &sc) const
     {
 #if defined(O_NONBLOCK) && !defined(_WIN32)
         int flags;
@@ -3678,12 +2566,12 @@
             flags |= O_NONBLOCK;
 
         if (fcntl(sc.handle(), F_SETFL, flags) < 0)
-            throw Error(Error::System, "fcntl");
+            throw Error();
 #else
         unsigned long flags = (m_value) ? 0 : 1;
 
         if (ioctlsocket(sc.handle(), FIONBIO, &flags) == Failure)
-            throw Error(Error::System, "fcntl");
+            throw Error();
 #endif
     }
 
@@ -3694,20 +2582,19 @@
      * \return the value
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    bool get(Socket<Address, Protocol> &sc) const
+    bool get(Socket &sc) const
     {
 #if defined(O_NONBLOCK) && !defined(_WIN32)
         int flags = fcntl(sc.handle(), F_GETFL, 0);
 
         if (flags < 0)
-            throw Error(Error::System, "fcntl");
+            throw Error();
 
         return !(flags & O_NONBLOCK);
 #else
         (void)sc;
 
-        throw Error(Error::Other, "get", std::strerror(ENOSYS));
+        throw Error(std::strerror(ENOSYS));
 #endif
     }
 };
@@ -3737,8 +2624,7 @@
      * \param sc the socket
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline void set(Socket<Address, Protocol> &sc) const
+    inline void set(Socket &sc) const
     {
         sc.set(SOL_SOCKET, SO_RCVBUF, m_value);
     }
@@ -3750,10 +2636,9 @@
      * \return the value
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline int get(Socket<Address, Protocol> &sc) const
+    inline int get(Socket &sc) const
     {
-        return sc.template get<int>(SOL_SOCKET, SO_RCVBUF);
+        return sc.get<int>(SOL_SOCKET, SO_RCVBUF);
     }
 };
 
@@ -3784,8 +2669,7 @@
      * \param sc the socket
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline void set(Socket<Address, Protocol> &sc) const
+    inline void set(Socket &sc) const
     {
         sc.set(SOL_SOCKET, SO_REUSEADDR, m_value ? 1 : 0);
     }
@@ -3797,10 +2681,9 @@
      * \return the value
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline bool get(Socket<Address, Protocol> &sc) const
+    inline bool get(Socket &sc) const
     {
-        return sc.template get<int>(SOL_SOCKET, SO_REUSEADDR) != 0;
+        return sc.get<int>(SOL_SOCKET, SO_REUSEADDR) != 0;
     }
 };
 
@@ -3829,8 +2712,7 @@
      * \param sc the socket
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline void set(Socket<Address, Protocol> &sc) const
+    inline void set(Socket &sc) const
     {
         sc.set(SOL_SOCKET, SO_SNDBUF, m_value);
     }
@@ -3842,10 +2724,9 @@
      * \return the value
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline int get(Socket<Address, Protocol> &sc) const
+    inline int get(Socket &sc) const
     {
-        return sc.template get<int>(SOL_SOCKET, SO_SNDBUF);
+        return sc.get<int>(SOL_SOCKET, SO_SNDBUF);
     }
 };
 
@@ -3876,8 +2757,7 @@
      * \param sc the socket
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline void set(Socket<Address, Protocol> &sc) const
+    inline void set(Socket &sc) const
     {
         sc.set(IPPROTO_TCP, TCP_NODELAY, m_value ? 1 : 0);
     }
@@ -3889,10 +2769,9 @@
      * \return the value
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline bool get(Socket<Address, Protocol> &sc) const
+    inline bool get(Socket &sc) const
     {
-        return sc.template get<int>(IPPROTO_TCP, TCP_NODELAY) != 0;
+        return sc.get<int>(IPPROTO_TCP, TCP_NODELAY) != 0;
     }
 };
 
@@ -3900,7 +2779,8 @@
  * \ingroup net-module-options
  * \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
+ * 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 {
@@ -3926,8 +2806,7 @@
      * \param sc the socket
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline void set(Socket<Address, Protocol> &sc) const
+    inline void set(Socket &sc) const
     {
         sc.set(IPPROTO_IPV6, IPV6_V6ONLY, m_value ? 1 : 0);
     }
@@ -3939,10 +2818,9 @@
      * \return the value
      * \throw Error on errors
      */
-    template <typename Address, typename Protocol>
-    inline bool get(Socket<Address, Protocol> &sc) const
+    inline bool get(Socket &sc) const
     {
-        return sc.template get<int>(IPPROTO_IPV6, IPV6_V6ONLY) != 0;
+        return sc.get<int>(IPPROTO_IPV6, IPV6_V6ONLY) != 0;
     }
 };
 
@@ -3956,8 +2834,8 @@
  */
 class ListenerStatus {
 public:
-    Handle socket;        //!< which socket is ready
-    Condition flags;    //!< the flags
+    Handle socket;              //!< which socket is ready
+    Condition flags;            //!< the flags
 };
 
 /**
@@ -4019,20 +2897,20 @@
         ev.data.fd = h;
 
         if (epoll_ctl(m_handle, op, h, &ev) < 0)
-            throw Error(Error::System, "epoll_ctl");
+            throw Error();
     }
 
 public:
     /**
      * Create epoll.
      *
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     inline Epoll()
         : m_handle(epoll_create1(0))
     {
         if (m_handle < 0)
-            throw Error(Error::System, "epoll_create");
+            throw Error();
     }
 
     /**
@@ -4066,15 +2944,15 @@
     }
 
     /**
-     * For set and unset, we need to apply the whole flags required, so if the socket
-     * was set to Connection::Readable and user *8adds** Connection::Writable, we must
-     * place both.
+     * For set and unset, we need to apply the whole flags required, so if the
+     * socket was set to Connection::Readable and user **adds**
+     * Connection::Writable, we must set both.
      *
      * \param table the listener table
      * \param h the handle
      * \param condition the condition
      * \param add set to true if the socket is new to the backend
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     void set(const ListenerTable &table, Handle h, Condition condition, bool add)
     {
@@ -4097,7 +2975,7 @@
      * \param h the handle
      * \param condition the condition
      * \param add set to true if the socket is new to the backend
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     void unset(const ListenerTable &table, Handle h, Condition condition, bool remove)
     {
@@ -4113,7 +2991,7 @@
      *
      * \param ms the milliseconds timeout
      * \return the sockets ready
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     std::vector<ListenerStatus> wait(const ListenerTable &, int ms)
     {
@@ -4121,9 +2999,9 @@
         std::vector<ListenerStatus> result;
 
         if (ret == 0)
-            throw Error(Error::Timeout, "epoll_wait", std::strerror(ETIMEDOUT));
+            throw TimeoutError();
         if (ret < 0)
-            throw Error(Error::System, "epoll_wait");
+            throw Error();
 
         for (int i = 0; i < ret; ++i)
             result.push_back(ListenerStatus{m_events[i].data.fd, toCondition(m_events[i].events)});
@@ -4172,20 +3050,20 @@
         EV_SET(&ev, h, filter, kflags, 0, 0, nullptr);
 
         if (kevent(m_handle, &ev, 1, nullptr, 0, nullptr) < 0)
-            throw Error(Error::System, "kevent");
+            throw Error();
     }
 
 public:
     /**
      * Create kqueue.
      *
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     inline Kqueue()
         : m_handle(kqueue())
     {
         if (m_handle < 0)
-            throw Error(Error::System, "kqueue");
+            throw Error();
     }
 
     /**
@@ -4224,7 +3102,7 @@
      * \param h the handle
      * \param condition the condition
      * \param add set to true if the socket is new to the backend
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     void set(const ListenerTable &, Handle h, Condition condition, bool add)
     {
@@ -4242,7 +3120,7 @@
      * \param h the handle
      * \param condition the condition
      * \param remove set to true if the socket is completely removed
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     void unset(const ListenerTable &, Handle h, Condition condition, bool remove)
     {
@@ -4259,7 +3137,7 @@
      *
      * \param ms the milliseconds timeout
      * \return the sockets ready
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     std::vector<ListenerStatus> wait(const ListenerTable &, int ms)
     {
@@ -4273,9 +3151,9 @@
         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));
+            throw TimeoutError();
         if (nevents < 0)
-            throw Error(Error::System, "kevent");
+            throw Error();
 
         for (int i = 0; i < nevents; ++i) {
             sockets.push_back(ListenerStatus{
@@ -4334,9 +3212,11 @@
         Condition condition = Condition::None;
 
         /*
-         * Poll implementations mark the socket differently regarding the disconnection of a socket.
+         * 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.
+         * 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;
@@ -4366,7 +3246,7 @@
      * \param h the handle
      * \param condition the condition
      * \param add set to true if the socket is new to the backend
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     void set(const ListenerTable &, Handle h, Condition condition, bool add)
     {
@@ -4387,7 +3267,7 @@
      * \param h the handle
      * \param condition the condition
      * \param remove set to true if the socket is completely removed
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     void unset(const ListenerTable &, Handle h, Condition condition, bool remove)
     {
@@ -4406,7 +3286,7 @@
      *
      * \param ms the milliseconds timeout
      * \return the sockets ready
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     std::vector<ListenerStatus> wait(const ListenerTable &, int ms)
     {
@@ -4417,9 +3297,9 @@
 #endif
 
         if (result == 0)
-            throw Error(Error::Timeout, "select", std::strerror(ETIMEDOUT));
+            throw TimeoutError();
         if (result < 0)
-            throw Error(Error::System, "poll");
+            throw Error();
 
         std::vector<ListenerStatus> sockets;
 
@@ -4437,7 +3317,8 @@
  * \ingroup net-module-backends
  * \brief Implements select(2)
  *
- * This class is the fallback of any other method, it is not preferred at all for many reasons.
+ * This class is the fallback of any other method, it is not preferred at all
+ * for many reasons.
  */
 class Select {
 public:
@@ -4471,7 +3352,7 @@
      * \param table the listener table
      * \param ms the milliseconds timeout
      * \return the sockets ready
-     * \throw net::Error on failures
+     * \throw Error on failures
      */
     std::vector<ListenerStatus> wait(const ListenerTable &table, int ms)
     {
@@ -4502,9 +3383,9 @@
         auto error = ::select(static_cast<int>(max + 1), &readset, &writeset, nullptr, towait);
 
         if (error == Failure)
-            throw Error(Error::System, "select");
+            throw Error();
         if (error == 0)
-            throw Error(Error::Timeout, "select", std::strerror(ETIMEDOUT));
+            throw TimeoutError();
 
         std::vector<ListenerStatus> sockets;
 
@@ -4691,7 +3572,8 @@
     /**
      * Remove completely the socket from the listener.
      *
-     * It is a shorthand for unset(sc, Condition::Readable | Condition::Writable);
+     * It is a shorthand for unset(sc, Condition::Readable |
+     * Condition::Writable);
      *
      * \param sc the socket
      */
@@ -4720,7 +3602,8 @@
     }
 
     /**
-     * Select a socket. Waits for a specific amount of time specified as the duration.
+     * Select a socket. Waits for a specific amount of time specified as the
+     * duration.
      *
      * \param duration the duration
      * \return the socket ready
@@ -4772,101 +3655,6 @@
 };
 
 /**
- * \ingroup net-module-tcp
- * \brief Helper to create TCP sockets.
- */
-template <typename Address>
-using SocketTcp = Socket<Address, protocol::Tcp>;
-
-/**
- * \ingroup net-module-tcp
- * \brief Helper to create TCP/Ipv4 or TCP/Ipv6 sockets.
- */
-using SocketTcpIp = Socket<address::Ip, protocol::Tcp>;
-
-/**
- * \ingroup net-module-tcp
- * \brief Helper to create TCP/Ipv4 sockets.
- */
-using SocketTcpIpv4 = Socket<address::Ipv4, protocol::Tcp>;
-
-/**
- * \ingroup net-module-tcp
- * \brief Helper to create TCP/Ipv6 sockets.
- */
-using SocketTcpIpv6 = Socket<address::Ipv6, protocol::Tcp>;
-
-/**
- * \ingroup net-module-udp
- * \brief Helper to create UDP sockets.
- */
-template <typename Address>
-using SocketUdp = Socket<Address, protocol::Udp>;
-
-/**
- * \ingroup net-module-udp
- * \brief Helper to create UDP/Ipv4 or UDP/Ipv6 sockets.
- */
-using SocketUdpIp = Socket<address::Ip, protocol::Udp>;
-
-/**
- * \ingroup net-module-udp
- * \brief Helper to create UDP/Ipv4 sockets.
- */
-using SocketUdpIpv4 = Socket<address::Ipv4, protocol::Udp>;
-
-/**
- * \ingroup net-module-udp
- * \brief Helper to create UDP/Ipv6 sockets.
- */
-using SocketUdpIpv6 = Socket<address::Ipv6, protocol::Udp>;
-
-#if !defined(_WIN32)
-
-/**
- * \ingroup net-module-tcp
- * \brief Helper to create TCP/Local sockets.
- */
-using SocketTcpLocal = Socket<address::Local, protocol::Tcp>;
-
-/**
- * \ingroup net-module-udp
- * \brief Helper to create UDP/Local sockets.
- */
-using SocketUdpLocal = Socket<address::Local, protocol::Udp>;
-
-#endif
-
-#if !defined(NET_NO_SSL)
-
-/**
- * \ingroup net-module-tls
- * \brief Helper to create TLS sockets.
- */
-template <typename Address>
-using SocketTls = Socket<Address, protocol::Tls>;
-
-/**
- * \ingroup net-module-tls
- * \brief Helper to create TLS/Ipv4 or TLS/Ipv6 sockets.
- */
-using SocketTlsIp = Socket<address::Ip, protocol::Tls>;
-
-/**
- * \ingroup net-module-tls
- * \brief Helper to create TLS/Ipv4 sockets.
- */
-using SocketTlsIpv4 = Socket<address::Ip, protocol::Tls>;
-
-/**
- * \ingroup net-module-tls
- * \brief Helper to create TLS/Ipv6 sockets.
- */
-using SocketTlsIpv6 = Socket<address::Ip, protocol::Tls>;
-
-#endif
-
-/**
  * \ingroup net-module-resolv
  *
  * Resolve an hostname immediately.
@@ -4876,12 +3664,15 @@
  * \param domain the domain (e.g. AF_INET)
  * \param type the type (e.g. SOCK_STREAM)
  * \return the address iterator
- * \throw net::Error on failures
+ * \throw Error on failures
  */
-inline address::AddressIterator resolve(const std::string &host, const std::string &service, int domain = AF_UNSPEC, int type = 0)
+inline AddressIterator resolve(const std::string &host,
+                               const std::string &service,
+                               int domain = AF_UNSPEC,
+                               int type = 0)
 {
 #if !defined(NET_NO_AUTO_INIT)
-        net::init();
+        init();
 #endif
 
     struct addrinfo hints, *res, *p;
@@ -4893,32 +3684,19 @@
     int e = getaddrinfo(host.c_str(), service.c_str(), &hints, &res);
 
     if (e != 0)
-        throw Error(Error::System, "getaddrinfo", gai_strerror(e));
-
-    std::vector<address::GenericAddress> addresses;
+        throw Error(gai_strerror(e));
+
+    std::vector<Address> addresses;
 
     for (p = res; p != nullptr; p = p->ai_next)
-        addresses.push_back(address::GenericAddress(p->ai_addr, p->ai_addrlen));
-
-    return address::AddressIterator(addresses, 0);
+        addresses.push_back(Address(p->ai_addr, p->ai_addrlen));
+
+    return AddressIterator(addresses, 0);
 }
 
 /**
- * Overloaded function.
+ * \ingroup net-module-resolv
  *
- * \param sc the parent socket
- * \param host the hostname
- * \param service the service name
- * \return the address iterator
- * \throw net::Error on failures
- */
-template <typename Address, typename Protocol>
-address::AddressIterator resolve(const Socket<Address, Protocol> &sc, const std::string &host, const std::string &service)
-{
-    return resolve(host, service, Address().domain(), sc.protocol().type());
-}
-
-/**
  * Resolve the first address.
  *
  * \param host the hostname
@@ -4926,37 +3704,22 @@
  * \param domain the domain (e.g. AF_INET)
  * \param type the type (e.g. SOCK_STREAM)
  * \return the first generic address available
- * \throw net::Error on failures
+ * \throw Error on failures
  * \note do not use AF_UNSPEC and 0 as type for this function
  */
-inline address::GenericAddress resolveOne(const std::string &host, const std::string &service, int domain, int type)
+inline Address resolveOne(const std::string &host, const std::string &service, int domain, int type)
 {
-    address::AddressIterator end;
-    address::AddressIterator it = resolve(host, service, domain, type);
+    AddressIterator it = resolve(host, service, domain, type);
+    AddressIterator end;
 
     if (it == end)
-        throw Error(Error::Other, "resolveOne", "no address available");
+        throw Error("no address available");
 
     return *it;
 }
 
-/**
- * Overloaded function
- *
- * \param sc the parent socket
- * \param host the hostname
- * \param service the service name
- * \return the first generic address available
- * \throw net::Error on failures
- */
-template <typename Address, typename Protocol>
-address::GenericAddress resolveOne(const Socket<Address, Protocol> &sc, const std::string &host, const std::string &service)
-{
-    return resolveOne(host, service, Address().domain(), sc.protocol().type());
-}
+} // !net
 
 } // !irccd
 
-} // !net
-
 #endif // !IRCCD_NET_HPP
--- a/lib/irccd/plugin-js.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/plugin-js.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -108,7 +108,7 @@
     }
 
     /**
-     * \copydoc Plugin::setConfig
+     * \copydoc Plugin::setFormats
      */
     void setFormats(PluginFormats formats) override
     {
--- a/lib/irccd/server-state-connected.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/server-state-connected.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -35,12 +35,12 @@
 class ConnectedState : public State {
 public:
     /**
-     * \copydoc ServerState::prepare
+     * \copydoc State::prepare
      */
     IRCCD_EXPORT void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
 
     /**
-     * \copydoc ServerState::ident
+     * \copydoc State::ident
      */
     IRCCD_EXPORT std::string ident() const override;
 };
--- a/lib/irccd/server-state-connecting.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/server-state-connecting.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -40,12 +40,12 @@
 
 public:
     /**
-     * \copydoc ServerState::prepare
+     * \copydoc State::prepare
      */
     IRCCD_EXPORT void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
 
     /**
-     * \copydoc ServerState::ident
+     * \copydoc State::ident
      */
     IRCCD_EXPORT std::string ident() const override;
 };
--- a/lib/irccd/server-state-disconnected.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/server-state-disconnected.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -39,12 +39,12 @@
 
 public:
     /**
-     * \copydoc ServerState::prepare
+     * \copydoc State::prepare
      */
     IRCCD_EXPORT void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
 
     /**
-     * \copydoc ServerState::ident
+     * \copydoc State::ident
      */
     IRCCD_EXPORT std::string ident() const override;
 };
--- a/lib/irccd/server-state.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/server-state.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -38,7 +38,6 @@
 class Server;
 
 /**
- * \class ServerState
  * \brief Server current state.
  */
 class State {
--- a/lib/irccd/server.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/server.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -676,7 +676,7 @@
     /**
      * Set the realname.
      *
-     * \param name the username
+     * \param realname the username
      * \note the username will be changed on the next connection
      */
     inline void setRealname(std::string realname) noexcept
--- a/lib/irccd/service-command.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/service-command.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -49,6 +49,11 @@
      */
     IRCCD_EXPORT CommandService();
 
+    inline const std::vector<std::shared_ptr<Command>> &commands() const noexcept
+    {
+        return m_commands;
+    }
+
     /**
      * Tells if a command exists.
      *
--- a/lib/irccd/service-interrupt.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/service-interrupt.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -16,20 +16,24 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <array>
+
 #include "logger.hpp"
 #include "service-interrupt.hpp"
 
 namespace irccd {
 
 InterruptService::InterruptService()
+    : m_in(AF_INET, 0)
+    , m_out(AF_INET, 0)
 {
     // Bind a socket to any port.
     m_in.set(net::option::SockReuseAddress(true));
-    m_in.bind(net::address::Ipv4("*", 0));
+    m_in.bind(net::ipv4::any(0));
     m_in.listen(1);
 
     // Do the socket pair.
-    m_out.connect(net::address::Ipv4("127.0.0.1", m_in.getsockname().port()));
+    m_out.connect(net::ipv4::pton("127.0.0.1", net::ipv4::port(m_in.getsockname())));
     m_in = m_in.accept();
     m_out.set(net::option::SockBlockMode(false));
 }
@@ -45,9 +49,11 @@
 void InterruptService::sync(fd_set &in, fd_set &)
 {
     if (FD_ISSET(m_in.handle(), &in)) {
+        static std::array<char, 32> tmp;
+
         try {
             log::debug("irccd: interrupt service recv");
-            m_in.recv(32);
+            m_in.recv(tmp.data(), 32);
         } catch (const std::exception &ex) {
             log::warning() << "irccd: interrupt service error: " << ex.what() << std::endl;
         }
@@ -57,8 +63,10 @@
 void InterruptService::interrupt() noexcept
 {
     try {
+        static char byte;
+
         log::debug("irccd: interrupt service send");
-        m_out.send(" ");
+        m_out.send(&byte, 1);
     } catch (const std::exception &ex) {
         log::warning() << "irccd: interrupt service error: " << ex.what() << std::endl;
     }
--- a/lib/irccd/service-interrupt.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/service-interrupt.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -34,8 +34,8 @@
  */
 class InterruptService : public Service {
 private:
-    net::SocketTcpIpv4 m_in;
-    net::SocketTcpIpv4 m_out;
+    net::TcpSocket m_in;
+    net::TcpSocket m_out;
 
 public:
     /**
--- a/lib/irccd/service-server.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/service-server.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -89,7 +89,7 @@
         { "channel",    ev.channel          },
         { "mode",       ev.mode             },
         { "argument",   ev.argument         }
-    }).dump());
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
         [=] (Plugin &) -> std::string {
@@ -114,7 +114,7 @@
         { "origin",     ev.origin           },
         { "channel",    ev.channel          },
         { "message",    ev.message          }
-    }).dump(0));
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
         [=] (Plugin &) -> std::string {
@@ -133,7 +133,7 @@
     m_irccd.transportService().broadcast(nlohmann::json::object({
         { "event",      "onConnect"         },
         { "server",     ev.server->name()   }
-    }).dump(0));
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), /* origin */ "", /* channel */ "",
         [=] (Plugin &) -> std::string {
@@ -157,7 +157,7 @@
         { "server",     ev.server->name()   },
         { "origin",     ev.origin           },
         { "channel",    ev.channel          }
-    }).dump(0));
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
         [=] (Plugin &) -> std::string {
@@ -180,7 +180,7 @@
         { "server",     ev.server->name()   },
         { "origin",     ev.origin           },
         { "channel",    ev.channel          }
-    }).dump(0));
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
         [=] (Plugin &) -> std::string {
@@ -207,7 +207,7 @@
         { "channel",    ev.channel          },
         { "target",     ev.target           },
         { "reason",     ev.reason           }
-    }).dump(0));
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
         [=] (Plugin &) -> std::string {
@@ -232,7 +232,7 @@
         { "origin",     ev.origin           },
         { "channel",    ev.channel          },
         { "message",    ev.message          }
-    }).dump(0));
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
         [=] (Plugin &plugin) -> std::string {
@@ -265,7 +265,7 @@
         { "origin",     ev.origin           },
         { "target",     ev.channel          },
         { "message",    ev.message          }
-    }).dump(0));
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
         [=] (Plugin &) -> std::string {
@@ -288,7 +288,7 @@
         { "server",     ev.server->name()   },
         { "origin",     ev.origin           },
         { "mode",       ev.mode             }
-    }).dump());
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "",
         [=] (Plugin &) -> std::string {
@@ -316,7 +316,7 @@
         { "server",     ev.server->name()   },
         { "channel",    ev.channel          },
         { "names",      std::move(names)    }
-    }).dump());
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), /* origin */ "", ev.channel,
         [=] (Plugin &) -> std::string {
@@ -339,7 +339,7 @@
         { "server",     ev.server->name()   },
         { "origin",     ev.origin           },
         { "nickname",   ev.nickname         }
-    }).dump());
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "",
         [=] (Plugin &) -> std::string {
@@ -362,7 +362,7 @@
         { "server",     ev.server->name()   },
         { "origin",     ev.origin           },
         { "message",    ev.message          }
-    }).dump());
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "",
         [=] (Plugin &) -> std::string {
@@ -387,7 +387,7 @@
         { "origin",     ev.origin           },
         { "channel",    ev.channel          },
         { "reason",     ev.reason           }
-    }).dump());
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
         [=] (Plugin &) -> std::string {
@@ -410,7 +410,7 @@
         { "server",     ev.server->name()   },
         { "origin",     ev.origin           },
         { "message",    ev.message          }
-    }).dump());
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, /* channel */ "",
         [=] (Plugin &plugin) -> std::string {
@@ -443,7 +443,7 @@
         { "origin",     ev.origin           },
         { "channel",    ev.channel          },
         { "topic",      ev.topic            }
-    }).dump());
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), ev.origin, ev.channel,
         [=] (Plugin &) -> std::string {
@@ -471,7 +471,7 @@
         { "username",   ev.whois.user       },
         { "host",       ev.whois.host       },
         { "realname",   ev.whois.realname   }
-    }).dump());
+    }));
 
     m_irccd.post(EventHandler{ev.server->name(), /* origin */ "", /* channel */ "",
         [=] (Plugin &) -> std::string {
--- a/lib/irccd/service-transport.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/service-transport.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -75,7 +75,7 @@
         response.push_back({"response", *name});
 
         // 5. Send the result.
-        tc->send(response.dump());
+        tc->send(response);
     });
 }
 
@@ -107,14 +107,8 @@
     }
 
     // Transport clients.
-    for (const auto &client : m_clients) {
-        FD_SET(client->handle(), &in);
-
-        if (client->hasOutput())
-            FD_SET(client->handle(), &out);
-        if (client->handle() > max)
-            max = client->handle();
-    }
+    for (const auto &client : m_clients)
+        client->prepare(in, out, max);
 }
 
 void TransportService::sync(fd_set &in, fd_set &out)
@@ -132,7 +126,7 @@
         std::weak_ptr<TransportClient> ptr(client);
 
         // Send some information.
-        nlohmann::json object = nlohmann::json::object({
+        auto object = nlohmann::json::object({
             { "program",    "irccd"                 },
             { "major",      IRCCD_VERSION_MAJOR     },
             { "minor",      IRCCD_VERSION_MINOR     },
@@ -146,19 +140,29 @@
         object.push_back({"ssl", true});
 #endif
 
-        client->send(object.dump());
+        try {
+            client->send(object);
 
-        // Connect signals.
-        client->onCommand.connect(std::bind(&TransportService::handleCommand, this, ptr, _1));
-        client->onDie.connect(std::bind(&TransportService::handleDie, this, ptr));
+            // Connect signals.
+            client->onCommand.connect(std::bind(&TransportService::handleCommand, this, ptr, _1));
+            client->onDie.connect(std::bind(&TransportService::handleDie, this, ptr));
 
-        // Register it.
-        m_clients.push_back(std::move(client));
+            // Register it.
+            m_clients.push_back(std::move(client));
+        } catch (const std::exception &ex) {
+            log::info() << "transport: client disconnected: " << ex.what() << std::endl;
+        }
     }
 
     // Transport clients.
-    for (const auto &client : m_clients)
-        client->sync(in, out);
+    for (const auto &client : m_clients) {
+        try {
+            client->sync(in, out);
+        } catch (const std::exception &ex) {
+            log::info() << "transport: client disconnected: " << ex.what() << std::endl;
+            handleDie(client);
+        }
+    }
 }
 
 void TransportService::add(std::shared_ptr<TransportServer> ts)
@@ -166,11 +170,13 @@
     m_servers.push_back(std::move(ts));
 }
 
-void TransportService::broadcast(std::string data)
+void TransportService::broadcast(const nlohmann::json &json)
 {
+    assert(json.is_object());
+
     // Asynchronous send.
     for (const auto &client : m_clients)
-        client->send(data);
+        client->send(json);
 }
 
 } // !irccd
--- a/lib/irccd/service-transport.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/service-transport.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -75,9 +75,10 @@
     /**
      * Send data to all clients.
      *
-     * \param data the data
+     * \pre object.is_object()
+     * \param object the json object
      */
-    IRCCD_EXPORT void broadcast(std::string data);
+    IRCCD_EXPORT void broadcast(const nlohmann::json &object);
 };
 
 } // !irccd
--- a/lib/irccd/service.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/service.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -78,6 +78,26 @@
     {
         util::unused(in, out);
     }
+
+    /**
+     * Convenient function for polling events with a timeout.
+     *
+     * \param timeout the timeout in milliseconds
+     */
+    virtual void poll(int timeout = -1)
+    {
+        fd_set in, out;
+        timeval tv = {0, timeout * 1000};
+
+        FD_ZERO(&in);
+        FD_ZERO(&out);
+
+        net::Handle max = 0;
+
+        prepare(in, out, max);
+        select(max + 1, &in, &out, nullptr, timeout < 0 ? nullptr : &tv);
+        sync(in, out);
+    }
 };
 
 } // !irccd
--- a/lib/irccd/transport-client.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/transport-client.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -20,35 +20,85 @@
 #include "logger.hpp"
 #include "transport-client.hpp"
 
-using json = nlohmann::json;
-
 namespace irccd {
 
 void TransportClient::parse(const std::string &message)
 {
-    auto document = json::parse(message);
+    auto document = nlohmann::json::parse(message);
+
+    if (document.is_object())
+        onCommand(document);
+}
+
+void TransportClient::receive()
+{
+    try {
+        std::string buffer;
 
-    if (!document.is_object())
-        throw std::invalid_argument("the message is not a valid JSON object");
+        buffer.resize(512);
+        buffer.resize(m_socket.recv(&buffer[0], buffer.size()));
 
-    onCommand(document);
+        if (buffer.empty())
+            onDie();
+
+        m_input += std::move(buffer);
+    } catch (const std::exception &) {
+        onDie();
+    }
 }
 
-void TransportClient::sync(fd_set &setinput, fd_set &setoutput)
+void TransportClient::send()
 {
-    if (FD_ISSET(handle(), &setinput)) {
+    try {
+        auto ns = m_socket.send(&m_output[0], m_output.size());
+
+        if (ns == 0)
+            onDie();
+
+        m_output.erase(0, ns);
+    } catch (const std::exception &ex) {
+        onDie();
+    }
+}
+
+void TransportClient::prepare(fd_set &in, fd_set &out, net::Handle &max)
+{
+    if (m_socket.handle() > max)
+        max = m_socket.handle();
+
+    FD_SET(m_socket.handle(), &in);
+
+    if (!m_output.empty())
+        FD_SET(m_socket.handle(), &out);
+}
+
+void TransportClient::sync(fd_set &in, fd_set &out)
+{
+    // Do some I/O.
+    if (FD_ISSET(m_socket.handle(), &in)) {
         log::debug() << "transport: receiving to input buffer" << std::endl;
         receive();
     }
-    if (FD_ISSET(handle(), &setoutput)) {
+    if (FD_ISSET(m_socket.handle(), &out)) {
         log::debug() << "transport: sending outgoing buffer" << std::endl;
         send();
     }
+
+    // Flush the queue.
+    for (std::size_t pos; (pos = m_input.find("\r\n\r\n")) != std::string::npos; ) {
+        auto message = m_input.substr(0, pos);
+
+        m_input.erase(m_input.begin(), m_input.begin() + pos + 4);
+
+        parse(message);
+    }
 }
 
-void TransportClient::send(std::string message)
+void TransportClient::send(const nlohmann::json &json)
 {
-    m_output += message;
+    assert(json.is_object());
+
+    m_output += json.dump();
     m_output += "\r\n\r\n";
 }
 
--- a/lib/irccd/transport-client.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/transport-client.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -64,8 +64,9 @@
     Signal<> onDie;
 
 protected:
-    std::string m_input;    //!< input buffer
-    std::string m_output;    //!< output buffer
+    net::TcpSocket m_socket;    //!< socket
+    std::string m_input;        //!< input buffer
+    std::string m_output;       //!< output buffer
 
     /**
      * Parse input buffer.
@@ -74,123 +75,27 @@
      */
     void parse(const std::string &buffer);
 
-    /**
-     * Start receiving data.
-     */
-    virtual void receive() = 0;
-
-    /**
-     * Start sending data.
-     */
-    virtual void send() = 0;
+private:
+    void receive();
+    void send();
 
 public:
+    inline TransportClient(net::TcpSocket socket)
+        : m_socket(std::move(socket))
+    {
+    }
+
     /**
      * Virtual destructor defaulted.
      */
     virtual ~TransportClient() = default;
 
-    /**
-     * Send or receive data, called after a select.
-     *
-     * \param setinput the input fd_set
-     * \param setoutput the output fd_set
-     */
-    IRCCD_EXPORT void sync(fd_set &setinput, fd_set &setoutput);
-
-    /**
-     * Send some data, it will be pushed to the outgoing buffer.
-     *
-     * This function appends "\r\n\r\n" after the message so you don't have
-     * to do it manually.
-     *
-     * \param message the message
-     */
-    IRCCD_EXPORT void send(std::string message);
-
-    /**
-     * Tell if the client has data pending for output.
-     *
-     * \return true if has pending data to write
-     */
-    inline bool hasOutput() const noexcept
-    {
-        return !m_output.empty();
-    }
-
-    /**
-     * Get the underlying socket handle.
-     *
-     * \return the socket
-     */
-    virtual net::Handle handle() noexcept = 0;
-};
-
-/**
- * \brief Template class for Tcp and Ssl sockets
- */
-template <typename Address>
-class TransportClientBase : public TransportClient {
-private:
-    net::SocketTcp<Address> m_socket;
-
-protected:
-    void send() override;
-    void receive() override;
+    IRCCD_EXPORT void send(const nlohmann::json &json);
 
-public:
-    /**
-     * Create a client.
-     *
-     * \param socket the socket
-     */
-    inline TransportClientBase(net::SocketTcp<Address> socket)
-        : m_socket(std::move(socket))
-    {
-    }
-
-    /**
-     * \copydoc TransportClient::handle
-     */
-    net::Handle handle() noexcept override
-    {
-        return m_socket.handle();
-    }
-};
-
-template <typename Address>
-void TransportClientBase<Address>::receive()
-{
-    try {
-        auto message = m_socket.recv(512);
+    IRCCD_EXPORT virtual void prepare(fd_set &in, fd_set &out, net::Handle &max);
 
-        if (message.empty())
-            onDie();
-
-        m_input += message;
-    } catch (const std::exception &) {
-        onDie();
-    }
-
-    std::string::size_type pos;
-    while ((pos = m_input.find("\r\n\r\n")) != std::string::npos) {
-        /*
-         * Make a copy and erase it in case that onComplete function
-         * throws.
-         */
-        auto message = m_input.substr(0, pos);
-
-        m_input.erase(m_input.begin(), m_input.begin() + pos + 4);
-
-        parse(message);
-    }
-}
-
-template <typename Address>
-void TransportClientBase<Address>::send()
-{
-    m_output.erase(0, m_socket.send(m_output));
-}
+    IRCCD_EXPORT virtual void sync(fd_set &in, fd_set &out);
+};
 
 } // !irccd
 
--- a/lib/irccd/transport-server.cpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/transport-server.cpp	Wed Aug 10 10:33:21 2016 +0200
@@ -30,33 +30,47 @@
 namespace irccd {
 
 /*
- * TransportServerIp
+ * TransportServerIpv6
  * ------------------------------------------------------------------
  */
 
-TransportServerIp::TransportServerIp(int domain, const std::string &address, int port, bool ipv6only)
-    : m_socket(domain, SOCK_STREAM, 0)
+TransportServerIpv6::TransportServerIpv6(const std::string &address, std::uint16_t port, bool ipv6only)
+    : TransportServer(net::TcpSocket(AF_INET6, 0))
 {
     m_socket.set(net::option::SockReuseAddress(true));
 
     // Disable or enable IPv4 when using IPv6.
-    if (domain == AF_INET6)
-        m_socket.set(net::option::Ipv6Only(ipv6only));
+    if (ipv6only)
+        m_socket.set(net::option::Ipv6Only(true));
 
-    m_socket.bind(net::address::Ip(address, port, domain));
+    if (address == "*")
+        m_socket.bind(net::ipv6::any(port));
+    else
+        m_socket.bind(net::ipv6::pton(address, port));
+
     m_socket.listen();
 
     log::info() << "transport: listening on " << address << ", port " << port << std::endl;
 }
 
-net::Handle TransportServerIp::handle() noexcept
+/*
+ * TransportServerIp
+ * ------------------------------------------------------------------
+ */
+
+TransportServerIp::TransportServerIp(const std::string &address, std::uint16_t port)
+    : TransportServer(net::TcpSocket(AF_INET, 0))
 {
-    return m_socket.handle();
-}
+    m_socket.set(net::option::SockReuseAddress(true));
 
-std::shared_ptr<TransportClient> TransportServerIp::accept()
-{
-    return std::make_shared<TransportClientBase<net::address::Ip>>(m_socket.accept());
+    if (address == "*")
+        m_socket.bind(net::ipv4::any(port));
+    else
+        m_socket.bind(net::ipv4::pton(address, port));
+
+    m_socket.listen();
+
+    log::info() << "transport: listening on " << address << ", port " << port << std::endl;
 }
 
 /*
@@ -66,30 +80,21 @@
 
 #if !defined(IRCCD_SYSTEM_WINDOWS)
 
-TransportServerUnix::TransportServerUnix(std::string path)
-    : m_path(std::move(path))
+TransportServerLocal::TransportServerLocal(std::string path)
+    : TransportServer(net::TcpSocket(AF_LOCAL, 0))
+    , m_path(std::move(path))
 {
-    m_socket.bind(net::address::Local{m_path, true});
+    m_socket.bind(net::local::create(m_path, true));
     m_socket.listen();
 
     log::info() << "transport: listening on " << m_path << std::endl;
 }
 
-TransportServerUnix::~TransportServerUnix()
+TransportServerLocal::~TransportServerLocal()
 {
     ::remove(m_path.c_str());
 }
 
-net::Handle TransportServerUnix::handle() noexcept
-{
-    return m_socket.handle();
-}
-
-std::shared_ptr<TransportClient> TransportServerUnix::accept()
-{
-    return std::make_shared<TransportClientBase<net::address::Local>>(m_socket.accept());
-}
-
 #endif
 
 } // !irccd
--- a/lib/irccd/transport-server.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/transport-server.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -34,8 +34,7 @@
 namespace irccd {
 
 /**
- * \class TransportServer
- * \brief Bring networking between irccd and irccdctl
+ * \brief Bring networking between irccd and irccdctl.
  *
  * This class contains a master sockets for listening to TCP connections, it is then processed by irccd.
  *
@@ -56,11 +55,30 @@
     TransportServer &operator=(const TransportServer &) = delete;
     TransportServer &operator=(TransportServer &&) = delete;
 
+protected:
+    /**
+     * The socket handle.
+     */
+    net::TcpSocket m_socket;
+
 public:
     /**
      * Default constructor.
      */
-    TransportServer() = default;
+    inline TransportServer(net::TcpSocket socket)
+        : m_socket(std::move(socket))
+    {
+    }
+
+    /**
+     * Get the socket handle for this transport.
+     *
+     * \return the handle
+     */
+    inline net::Handle handle() const noexcept
+    {
+        return m_socket.handle();
+    }
 
     /**
      * Destructor defaulted.
@@ -68,63 +86,52 @@
     virtual ~TransportServer() = default;
 
     /**
-     * Retrieve the underlying socket handle.
-     *
-     * \return the socket
-     */
-    virtual net::Handle handle() noexcept = 0;
-
-    /**
      * Accept a new client depending on the domain.
      *
      * \return the new client
      */
-    virtual std::shared_ptr<TransportClient> accept() = 0;
+    virtual std::unique_ptr<TransportClient> accept()
+    {
+        return std::make_unique<TransportClient>(m_socket.accept());
+    }
 };
 
 /**
- * \class TransportServerIp
- * \brief Base class for both IPv4 and IPv6 servers.
+ * \brief Create IP transport.
  */
 class TransportServerIp : public TransportServer {
-protected:
-    /**
-     * The TCP/IP socket.
-     */
-    net::SocketTcp<net::address::Ip> m_socket;
-
 public:
     /**
-     * Create a IP transport, use IPv6 or IPv4 address.
+     * Constructor.
      *
-     * \param domain AF_INET or AF_INET6
-     * \param address the address or "*" for any
+     * \param address the address (* for any)
      * \param port the port number
-     * \param ipv6only set to true to disable IPv4
-     * \throw net::Error on failures
      */
-    IRCCD_EXPORT TransportServerIp(int domain, const std::string &address, int port, bool ipv6only = true);
+    IRCCD_EXPORT TransportServerIp(const std::string &address, std::uint16_t port);
+};
 
+/**
+ * \brief Create IPv6 transport.
+ */
+class TransportServerIpv6 : public TransportServer {
+public:
     /**
-     * \copydoc TransportServer::handle
+     * Constructor.
+     *
+     * \param address the address (* for any)
+     * \param port the port number
+     * \param ipv6only set to true to disable ipv4 completely
      */
-    IRCCD_EXPORT net::Handle handle() noexcept override;
-
-    /**
-     * \copydoc TransportServer::accept
-     */
-    IRCCD_EXPORT std::shared_ptr<TransportClient> accept() override;
+    IRCCD_EXPORT TransportServerIpv6(const std::string &address, std::uint16_t port, bool ipv6only = true);
 };
 
 #if !defined(IRCCD_SYSTEM_WINDOWS)
 
 /**
- * \class TransportServerUnix
  * \brief Implementation of transports for Unix sockets.
  */
-class TransportServerUnix : public TransportServer {
+class TransportServerLocal : public TransportServer {
 private:
-    net::SocketTcp<net::address::Local> m_socket;
     std::string m_path;
 
 public:
@@ -133,22 +140,12 @@
      *
      * \param path the path
      */
-    IRCCD_EXPORT TransportServerUnix(std::string path);
+    IRCCD_EXPORT TransportServerLocal(std::string path);
 
     /**
      * Destroy the transport and remove the file.
      */
-    IRCCD_EXPORT ~TransportServerUnix();
-
-    /**
-     * \copydoc TransportServer::handle
-     */
-    IRCCD_EXPORT net::Handle handle() noexcept override;
-
-    /**
-     * \copydoc TransportServer::accept
-     */
-    IRCCD_EXPORT std::shared_ptr<TransportClient> accept() override;
+    IRCCD_EXPORT ~TransportServerLocal();
 };
 
 #endif // !_WIN32
--- a/lib/irccd/util.hpp	Mon Jul 18 22:39:14 2016 +0200
+++ b/lib/irccd/util.hpp	Wed Aug 10 10:33:21 2016 +0200
@@ -543,8 +543,9 @@
  *
  * \param json the json value
  * \param key the property key
- * \param def the default value
- * \return the boolean
+ * \param min the minimum value
+ * \param max the maximum value
+ * \return the value
  */
 template <typename T>
 inline T getIntRange(const nlohmann::json &json,
@@ -560,8 +561,9 @@
  *
  * \param json the json value
  * \param key the property key
- * \param def the default value
- * \return the boolean
+ * \param min the minimum value
+ * \param max the maximum value
+ * \return value
  */
 template <typename T>
 inline T getUintRange(const nlohmann::json &json,