changeset 246:62807bf9a68c

Irccd: reduce server stuff
author David Demelier <markand@malikania.fr>
date Wed, 17 Aug 2016 20:41:25 +0200
parents 520bf779bf28
children 4dec71ea1326
files lib/irccd/CMakeSources.cmake lib/irccd/server-private.hpp lib/irccd/server-state-connected.cpp lib/irccd/server-state-connected.hpp lib/irccd/server-state-connecting.cpp lib/irccd/server-state-connecting.hpp lib/irccd/server-state-disconnected.cpp lib/irccd/server-state-disconnected.hpp lib/irccd/server-state.hpp lib/irccd/server.cpp
diffstat 10 files changed, 241 insertions(+), 563 deletions(-) [+]
line wrap: on
line diff
--- a/lib/irccd/CMakeSources.cmake	Wed Aug 17 18:35:31 2016 +0200
+++ b/lib/irccd/CMakeSources.cmake	Wed Aug 17 20:41:25 2016 +0200
@@ -55,11 +55,6 @@
     ${CMAKE_CURRENT_LIST_DIR}/plugin-js.hpp
     ${CMAKE_CURRENT_LIST_DIR}/rule.hpp
     ${CMAKE_CURRENT_LIST_DIR}/server.hpp
-    ${CMAKE_CURRENT_LIST_DIR}/server-private.hpp
-    ${CMAKE_CURRENT_LIST_DIR}/server-state.hpp
-    ${CMAKE_CURRENT_LIST_DIR}/server-state-connected.hpp
-    ${CMAKE_CURRENT_LIST_DIR}/server-state-connecting.hpp
-    ${CMAKE_CURRENT_LIST_DIR}/server-state-disconnected.hpp
     ${CMAKE_CURRENT_LIST_DIR}/service.hpp
     ${CMAKE_CURRENT_LIST_DIR}/service-command.hpp
     ${CMAKE_CURRENT_LIST_DIR}/service-interrupt.hpp
@@ -129,9 +124,6 @@
     ${CMAKE_CURRENT_LIST_DIR}/plugin-js.cpp
     ${CMAKE_CURRENT_LIST_DIR}/rule.cpp
     ${CMAKE_CURRENT_LIST_DIR}/server.cpp
-    ${CMAKE_CURRENT_LIST_DIR}/server-state-connected.cpp
-    ${CMAKE_CURRENT_LIST_DIR}/server-state-connecting.cpp
-    ${CMAKE_CURRENT_LIST_DIR}/server-state-disconnected.cpp
     ${CMAKE_CURRENT_LIST_DIR}/service-command.cpp
     ${CMAKE_CURRENT_LIST_DIR}/service-interrupt.cpp
     ${CMAKE_CURRENT_LIST_DIR}/service-module.cpp
--- a/lib/irccd/server-private.hpp	Wed Aug 17 18:35:31 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * server-private.hpp -- libircclient bridge
- *
- * 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_SERVER_PRIVATE_HPP
-#define IRCCD_SERVER_PRIVATE_HPP
-
-#include <memory>
-
-#include <libircclient.h>
-
-#include "server.hpp"
-
-namespace irccd {
-
-/**
- * \brief Bridge for libircclient
- */
-class Server::Session {
-public:
-    /**
-     * libircclient handle.
-     */
-    using Handle = std::unique_ptr<irc_session_t, void (*)(irc_session_t *)>;
-
-private:
-    Handle m_handle;
-
-public:
-    /**
-     * Create a null session.
-     */
-    inline Session()
-        : m_handle(nullptr, nullptr)
-    {
-    }
-
-    /**
-     * Convert the libircclient session.
-     */
-    inline operator const irc_session_t *() const noexcept
-    {
-        return m_handle.get();
-    }
-
-    /**
-     * Overloaded function.
-     */
-    inline operator irc_session_t *() noexcept
-    {
-        return m_handle.get();
-    }
-
-    /**
-     * Get the handle.
-     *
-     * \return the handle
-     */
-    inline Handle &handle() noexcept
-    {
-        return m_handle;
-    }
-
-    /**
-     * Tells if the connection is made.
-     *
-     * \return true if connected
-     */
-    inline bool isConnected() const noexcept
-    {
-        return irc_is_connected(m_handle.get());
-    }
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVER_PRIVATE_HPP
--- a/lib/irccd/server-state-connected.cpp	Wed Aug 17 18:35:31 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * server-state-connected.cpp -- connected 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 <format.h>
-
-#include "logger.hpp"
-#include "server-state-connected.hpp"
-#include "server-state-disconnected.hpp"
-#include "server-private.hpp"
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-void Server::ConnectedState::prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
-{
-    if (!irc_is_connected(*server.m_session)) {
-        log::warning() << "server " << server.m_name << ": disconnected" << std::endl;
-
-        if (server.m_recodelay > 0)
-            log::warning("server {}: retrying in {} seconds"_format(server.m_name, server.m_recodelay));
-
-        server.next(std::make_unique<DisconnectedState>());
-    } else if (server.m_timer.elapsed() >= server.m_timeout * 1000) {
-        log::warning() << "server " << server.m_name << ": ping timeout after "
-                   << (server.m_timer.elapsed() / 1000) << " seconds" << std::endl;
-        server.next(std::make_unique<DisconnectedState>());
-    } else
-        irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
-}
-
-std::string Server::ConnectedState::ident() const
-{
-    return "Connected";
-}
-
-} // !irccd
--- a/lib/irccd/server-state-connected.hpp	Wed Aug 17 18:35:31 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * server-state-connected.hpp -- connected 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_SERVER_STATE_CONNECTED_HPP
-#define IRCCD_SERVER_STATE_CONNECTED_HPP
-
-/**
- * \file server-state-connected.hpp
- * \brief Connected state.
- */
-
-#include "server-state.hpp"
-
-namespace irccd {
-
-/**
- * \brief Connected state.
- * \ingroup states
- */
-class Server::ConnectedState : public State {
-public:
-    /**
-     * \copydoc State::prepare
-     */
-    IRCCD_EXPORT void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
-
-    /**
-     * \copydoc State::ident
-     */
-    IRCCD_EXPORT std::string ident() const override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVER_STATE_CONNECTED_HPP
--- a/lib/irccd/server-state-connecting.cpp	Wed Aug 17 18:35:31 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-/*
- * server-state-connecting.cpp -- connecting 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 <format.h>
-
-#include "logger.hpp"
-#include "server-state-connecting.hpp"
-#include "server-state-connected.hpp"
-#include "server-state-disconnected.hpp"
-#include "server-private.hpp"
-#include "sysconfig.hpp"
-
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-#  include <sys/types.h>
-#  include <netinet/in.h>
-#  include <arpa/nameser.h>
-#  include <resolv.h>
-#endif
-
-using namespace fmt::literals;
-
-namespace irccd {
-
-bool Server::ConnectingState::connect(Server &server)
-{
-    const char *password = server.m_password.empty() ? nullptr : server.m_password.c_str();
-    std::string host = server.m_host;
-    int code;
-
-    // libircclient requires # for SSL connection.
-#if defined(WITH_SSL)
-    if (server.m_flags & Server::Ssl)
-        host.insert(0, 1, '#');
-    if (!(server.m_flags & Server::SslVerify))
-        irc_option_set(*server.m_session, LIBIRC_OPTION_SSL_NO_VERIFY);
-#endif
-
-    if (server.flags() & Server::Ipv6) {
-        code = irc_connect6(*server.m_session, host.c_str(), server.m_port, password,
-                            server.m_nickname.c_str(),
-                            server.m_username.c_str(),
-                            server.m_realname.c_str());
-    } else {
-        code = irc_connect(*server.m_session, host.c_str(), server.m_port, password,
-                           server.m_nickname.c_str(),
-                           server.m_username.c_str(),
-                           server.m_realname.c_str());
-    }
-
-    return code == 0;
-}
-
-void Server::ConnectingState::prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
-{
-    /*
-     * The connect function will either fail if the hostname wasn't resolved or if any of the internal functions
-     * fail.
-     *
-     * It returns success if the connection was successful but it does not mean that connection is established.
-     *
-     * Because this function will be called repeatidly, the connection was started and we're still not
-     * connected in the specified timeout time, we mark the server as disconnected.
-     *
-     * Otherwise, the libircclient event_connect will change the state.
-     */
-    if (m_started) {
-        if (m_timer.elapsed() > static_cast<unsigned>(server.m_recodelay * 1000)) {
-            log::warning() << "server " << server.name() << ": timeout while connecting" << std::endl;
-            server.next(std::make_unique<DisconnectedState>());
-        } else if (!irc_is_connected(*server.m_session)) {
-            log::warning() << "server " << server.m_name << ": error while connecting: ";
-            log::warning() << irc_strerror(irc_errno(*server.m_session)) << std::endl;
-
-            if (server.m_recotries != 0)
-                log::warning("server {}: retrying in {} seconds"_format(server.m_name, server.m_recodelay));
-
-            server.next(std::make_unique<DisconnectedState>());
-        } else
-            irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
-    } else {
-        /*
-         * This is needed if irccd is started before DHCP or if DNS cache is outdated.
-         *
-         * For more information see bug #190.
-         */
-#if !defined(IRCCD_SYSTEM_WINDOWS)
-        (void)res_init();
-#endif
-        log::info("server {}: trying to connect to {}, port {}"_format(server.m_name, server.m_host, server.m_port));
-
-        if (!connect(server)) {
-            log::warning() << "server " << server.m_name << ": disconnected while connecting: ";
-            log::warning() << irc_strerror(irc_errno(*server.m_session)) << std::endl;
-            server.next(std::make_unique<DisconnectedState>());
-        } else {
-            m_started = true;
-
-            if (irc_is_connected(*server.m_session))
-                irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
-        }
-    }
-}
-
-std::string Server::ConnectingState::ident() const
-{
-    return "Connecting";
-}
-
-} // !irccd
--- a/lib/irccd/server-state-connecting.hpp	Wed Aug 17 18:35:31 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * server-state-connecting.hpp -- connecting 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_SERVER_STATE_CONNECTING_HPP
-#define IRCCD_SERVER_STATE_CONNECTING_HPP
-
-/**
- * \file server-state-connecting.hpp
- * \brief Connecting state.
- */
-
-#include "elapsed-timer.hpp"
-#include "server-state.hpp"
-
-namespace irccd {
-
-/**
- * \brief Connecting state.
- * \ingroup states
- */
-class Server::ConnectingState : public State {
-private:
-    bool m_started{false};
-    ElapsedTimer m_timer;
-
-    bool connect(Server &server);
-
-public:
-    /**
-     * \copydoc State::prepare
-     */
-    IRCCD_EXPORT void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
-
-    /**
-     * \copydoc State::ident
-     */
-    IRCCD_EXPORT std::string ident() const override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVER_STATE_CONNECTING_HPP
--- a/lib/irccd/server-state-disconnected.cpp	Wed Aug 17 18:35:31 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * server-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 "logger.hpp"
-#include "server-state-connecting.hpp"
-#include "server-state-disconnected.hpp"
-#include "server-private.hpp"
-
-namespace irccd {
-
-void Server::DisconnectedState::prepare(Server &server, fd_set &, fd_set &, net::Handle &)
-{
-    if (server.m_recotries == 0) {
-        log::warning() << "server " << server.m_name << ": reconnection disabled, skipping" << std::endl;
-        server.onDie();
-    } else if (server.m_recotries > 0 && server.m_recocur > server.m_recotries) {
-        log::warning() << "server " << server.m_name << ": giving up" << std::endl;
-        server.onDie();
-    } else {
-        if (m_timer.elapsed() > static_cast<unsigned>(server.m_recodelay * 1000)) {
-            irc_disconnect(*server.m_session);
-
-            server.m_recocur ++;
-            server.next(std::make_unique<ConnectingState>());
-        }
-    }
-}
-
-std::string Server::DisconnectedState::ident() const
-{
-    return "Disconnected";
-}
-
-} // !irccd
--- a/lib/irccd/server-state-disconnected.hpp	Wed Aug 17 18:35:31 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * server-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_SERVER_STATE_DISCONNECTED_HPP
-#define IRCCD_SERVER_STATE_DISCONNECTED_HPP
-
-/**
- * \file server-state-disconnected.hpp
- * \brief Connecting state.
- */
-
-#include "elapsed-timer.hpp"
-#include "server-state.hpp"
-
-namespace irccd {
-
-/**
- * \brief Disconnected state.
- * \ingroup states
- */
-class Server::DisconnectedState : public Server::State {
-private:
-    ElapsedTimer m_timer;
-
-public:
-    /**
-     * \copydoc State::prepare
-     */
-    IRCCD_EXPORT void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) override;
-
-    /**
-     * \copydoc State::ident
-     */
-    IRCCD_EXPORT std::string ident() const override;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVER_STATE_DISCONNECTED_HPP
--- a/lib/irccd/server-state.hpp	Wed Aug 17 18:35:31 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-/*
- * server-state.hpp -- server current 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_SERVER_STATE_HPP
-#define IRCCD_SERVER_STATE_HPP
-
-/**
- * \file server-state.hpp
- * \brief Server state.
- */
-
-/**
- * \defgroup states Server states
- * \brief States for Server class.
- */
-
-#include "elapsed-timer.hpp"
-#include "net.hpp"
-#include "sysconfig.hpp"
-#include "server.hpp"
-
-namespace irccd {
-
-/**
- * \brief Server current state.
- */
-class Server::State {
-public:
-    /**
-     * Default constructor.
-     */
-    State() = default;
-
-    /**
-     * Virtual default destructor.
-     */
-    virtual ~State() = default;
-
-    /**
-     * Prepare the state.
-     *
-     * \param server the server
-     * \param setinput the read set
-     * \param setoutput the write set
-     * \param maxfd the maximum fd
-     */
-    virtual void prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd) = 0;
-
-    /**
-     * Return the state identifier, only for information purposes.
-     *
-     * \return the identifier
-     */
-    virtual std::string ident() const = 0;
-};
-
-} // !irccd
-
-#endif // !IRCCD_SERVER_STATE_HPP
--- a/lib/irccd/server.cpp	Wed Aug 17 18:35:31 2016 +0200
+++ b/lib/irccd/server.cpp	Wed Aug 17 20:41:25 2016 +0200
@@ -23,18 +23,110 @@
 
 #include <format.h>
 
+#include <libircclient.h>
 #include <libirc_rfcnumeric.h>
 
+#include "sysconfig.hpp"
+
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+#  include <sys/types.h>
+#  include <netinet/in.h>
+#  include <arpa/nameser.h>
+#  include <resolv.h>
+#endif
+
 #include "logger.hpp"
-#include "server-private.hpp"
-#include "server-state-connected.hpp"
-#include "server-state-connecting.hpp"
 #include "util.hpp"
+#include "server.hpp"
 
 using namespace fmt::literals;
 
 namespace irccd {
 
+/*
+ * Server::Session declaration.
+ * ------------------------------------------------------------------
+ */
+
+class Server::Session {
+public:
+    std::unique_ptr<irc_session_t, void (*)(irc_session_t *)> m_handle{nullptr, nullptr};
+
+    inline operator const irc_session_t *() const noexcept
+    {
+        return m_handle.get();
+    }
+
+    inline operator irc_session_t *() noexcept
+    {
+        return m_handle.get();
+    }
+
+    inline bool isConnected() const noexcept
+    {
+        return irc_is_connected(m_handle.get());
+    }
+};
+
+/*
+ * Server::State declaration.
+ * ------------------------------------------------------------------
+ */
+
+class Server::State {
+public:
+    State() = default;
+    virtual ~State() = default;
+    virtual void prepare(Server &, fd_set &, fd_set &, net::Handle &) = 0;
+    virtual std::string ident() const = 0;
+};
+
+/*
+ * Server::DisconnectedState declaration.
+ * ------------------------------------------------------------------
+ */
+
+class Server::DisconnectedState : public Server::State {
+private:
+    ElapsedTimer m_timer;
+
+public:
+    void prepare(Server &, fd_set &, fd_set &, net::Handle &) override;
+    std::string ident() const override;
+};
+
+/*
+ * Server::ConnectingState declaration.
+ * ------------------------------------------------------------------
+ */
+
+class Server::ConnectingState : public State {
+private:
+    enum {
+        Disconnected,
+        Connecting
+    } m_state{Disconnected};
+
+    ElapsedTimer m_timer;
+
+    bool connect(Server &server);
+
+public:
+    void prepare(Server &, fd_set &, fd_set &, net::Handle &) override;
+    std::string ident() const override;
+};
+
+/*
+ * Server::ConnectedState declaration.
+ * ------------------------------------------------------------------
+ */
+
+class Server::ConnectedState : public State {
+public:
+    void prepare(Server &, fd_set &, fd_set &, net::Handle &) override;
+    std::string ident() const override;
+};
+
 namespace {
 
 /*
@@ -459,7 +551,7 @@
         static_cast<Server *>(irc_get_ctx(session))->handleMode(orig, params);
     };
 
-    m_session->handle() = Session::Handle{irc_create_session(&callbacks), irc_destroy_session};
+    m_session->m_handle = {irc_create_session(&callbacks), irc_destroy_session};
 
     // Save this to the session.
     irc_set_ctx(*m_session, this);
@@ -665,4 +757,149 @@
     });
 }
 
+/*
+ * Server::DisconnectedState implementation
+ * ------------------------------------------------------------------
+ */
+
+void Server::DisconnectedState::prepare(Server &server, fd_set &, fd_set &, net::Handle &)
+{
+    if (server.m_recotries == 0) {
+        log::warning() << "server " << server.m_name << ": reconnection disabled, skipping" << std::endl;
+        server.onDie();
+    } else if (server.m_recotries > 0 && server.m_recocur > server.m_recotries) {
+        log::warning() << "server " << server.m_name << ": giving up" << std::endl;
+        server.onDie();
+    } else {
+        if (m_timer.elapsed() > static_cast<unsigned>(server.m_recodelay * 1000)) {
+            irc_disconnect(*server.m_session);
+
+            server.m_recocur ++;
+            server.next(std::make_unique<ConnectingState>());
+        }
+    }
+}
+
+std::string Server::DisconnectedState::ident() const
+{
+    return "Disconnected";
+}
+
+/*
+ * Server::ConnectingState implementation
+ * ------------------------------------------------------------------
+ */
+
+bool Server::ConnectingState::connect(Server &server)
+{
+    const char *password = server.m_password.empty() ? nullptr : server.m_password.c_str();
+    std::string host = server.m_host;
+    int code;
+
+    // libircclient requires # for SSL connection.
+#if defined(WITH_SSL)
+    if (server.m_flags & Server::Ssl)
+        host.insert(0, 1, '#');
+    if (!(server.m_flags & Server::SslVerify))
+        irc_option_set(*server.m_session, LIBIRC_OPTION_SSL_NO_VERIFY);
+#endif
+
+    if (server.flags() & Server::Ipv6) {
+        code = irc_connect6(*server.m_session, host.c_str(), server.m_port, password,
+                            server.m_nickname.c_str(),
+                            server.m_username.c_str(),
+                            server.m_realname.c_str());
+    } else {
+        code = irc_connect(*server.m_session, host.c_str(), server.m_port, password,
+                           server.m_nickname.c_str(),
+                           server.m_username.c_str(),
+                           server.m_realname.c_str());
+    }
+
+    return code == 0;
+}
+
+void Server::ConnectingState::prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
+{
+    /*
+     * The connect function will either fail if the hostname wasn't resolved or if any of the internal functions
+     * fail.
+     *
+     * It returns success if the connection was successful but it does not mean that connection is established.
+     *
+     * Because this function will be called repeatidly, the connection was started and we're still not
+     * connected in the specified timeout time, we mark the server as disconnected.
+     *
+     * Otherwise, the libircclient event_connect will change the state.
+     */
+    if (m_state == Disconnected) {
+        if (m_timer.elapsed() > static_cast<unsigned>(server.m_recodelay * 1000)) {
+            log::warning() << "server " << server.name() << ": timeout while connecting" << std::endl;
+            server.next(std::make_unique<DisconnectedState>());
+        } else if (!irc_is_connected(*server.m_session)) {
+            log::warning() << "server " << server.m_name << ": error while connecting: ";
+            log::warning() << irc_strerror(irc_errno(*server.m_session)) << std::endl;
+
+            if (server.m_recotries != 0)
+                log::warning("server {}: retrying in {} seconds"_format(server.m_name, server.m_recodelay));
+
+            server.next(std::make_unique<DisconnectedState>());
+        } else
+            irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
+    } else {
+        /*
+         * This is needed if irccd is started before DHCP or if DNS cache is outdated.
+         *
+         * For more information see bug #190.
+         */
+#if !defined(IRCCD_SYSTEM_WINDOWS)
+        (void)res_init();
+#endif
+        log::info("server {}: trying to connect to {}, port {}"_format(server.m_name, server.m_host, server.m_port));
+
+        if (!connect(server)) {
+            log::warning() << "server " << server.m_name << ": disconnected while connecting: ";
+            log::warning() << irc_strerror(irc_errno(*server.m_session)) << std::endl;
+            server.next(std::make_unique<DisconnectedState>());
+        } else {
+            m_state = Connecting;
+
+            if (irc_is_connected(*server.m_session))
+                irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
+        }
+    }
+}
+
+std::string Server::ConnectingState::ident() const
+{
+    return "Connecting";
+}
+
+/*
+ * Server::ConnectedState implementation
+ * ------------------------------------------------------------------
+ */
+
+void Server::ConnectedState::prepare(Server &server, fd_set &setinput, fd_set &setoutput, net::Handle &maxfd)
+{
+    if (!irc_is_connected(*server.m_session)) {
+        log::warning() << "server " << server.m_name << ": disconnected" << std::endl;
+
+        if (server.m_recodelay > 0)
+            log::warning("server {}: retrying in {} seconds"_format(server.m_name, server.m_recodelay));
+
+        server.next(std::make_unique<DisconnectedState>());
+    } else if (server.m_timer.elapsed() >= server.m_timeout * 1000) {
+        log::warning() << "server " << server.m_name << ": ping timeout after "
+                   << (server.m_timer.elapsed() / 1000) << " seconds" << std::endl;
+        server.next(std::make_unique<DisconnectedState>());
+    } else
+        irc_add_select_descriptors(*server.m_session, &setinput, &setoutput, reinterpret_cast<int *>(&maxfd));
+}
+
+std::string Server::ConnectedState::ident() const
+{
+    return "Connected";
+}
+
 } // !irccd