From 0ab40dc1ab4e689921da54080b135e1d22b1c586 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Tue, 14 Mar 2017 21:45:23 +0100
Subject: Refactoring louloulibs and cmake

Use OBJECT libraries
Remove the louloulibs directory
Write FOUND variables in the cache
---
 src/network/credentials_manager.cpp       | 140 ++++++++++++
 src/network/credentials_manager.hpp       |  55 +++++
 src/network/dns_handler.cpp               |  46 ++++
 src/network/dns_handler.hpp               |  37 +++
 src/network/dns_socket_handler.cpp        |  43 ++++
 src/network/dns_socket_handler.hpp        |  33 +++
 src/network/poller.cpp                    | 234 +++++++++++++++++++
 src/network/poller.hpp                    |  98 ++++++++
 src/network/resolver.cpp                  | 281 +++++++++++++++++++++++
 src/network/resolver.hpp                  | 122 ++++++++++
 src/network/socket_handler.hpp            |  42 ++++
 src/network/tcp_client_socket_handler.cpp | 261 ++++++++++++++++++++++
 src/network/tcp_client_socket_handler.hpp |  82 +++++++
 src/network/tcp_server_socket.hpp         |  70 ++++++
 src/network/tcp_socket_handler.cpp        | 358 ++++++++++++++++++++++++++++++
 src/network/tcp_socket_handler.hpp        | 251 +++++++++++++++++++++
 16 files changed, 2153 insertions(+)
 create mode 100644 src/network/credentials_manager.cpp
 create mode 100644 src/network/credentials_manager.hpp
 create mode 100644 src/network/dns_handler.cpp
 create mode 100644 src/network/dns_handler.hpp
 create mode 100644 src/network/dns_socket_handler.cpp
 create mode 100644 src/network/dns_socket_handler.hpp
 create mode 100644 src/network/poller.cpp
 create mode 100644 src/network/poller.hpp
 create mode 100644 src/network/resolver.cpp
 create mode 100644 src/network/resolver.hpp
 create mode 100644 src/network/socket_handler.hpp
 create mode 100644 src/network/tcp_client_socket_handler.cpp
 create mode 100644 src/network/tcp_client_socket_handler.hpp
 create mode 100644 src/network/tcp_server_socket.hpp
 create mode 100644 src/network/tcp_socket_handler.cpp
 create mode 100644 src/network/tcp_socket_handler.hpp

(limited to 'src/network')

diff --git a/src/network/credentials_manager.cpp b/src/network/credentials_manager.cpp
new file mode 100644
index 0000000..f9f8c94
--- /dev/null
+++ b/src/network/credentials_manager.cpp
@@ -0,0 +1,140 @@
+#include "biboumi.h"
+
+#ifdef BOTAN_FOUND
+#include <network/tcp_socket_handler.hpp>
+#include <network/credentials_manager.hpp>
+#include <logger/logger.hpp>
+#include <botan/tls_exceptn.h>
+#include <config/config.hpp>
+
+#ifdef USE_DATABASE
+# include <database/database.hpp>
+#endif
+
+/**
+ * TODO find a standard way to find that out.
+ */
+static const std::vector<std::string> default_cert_files = {
+    "/etc/ssl/certs/ca-bundle.crt",
+    "/etc/pki/tls/certs/ca-bundle.crt",
+    "/etc/ssl/certs/ca-certificates.crt",
+    "/etc/ca-certificates/extracted/tls-ca-bundle.pem"
+};
+
+Botan::Certificate_Store_In_Memory BasicCredentialsManager::certificate_store;
+bool BasicCredentialsManager::certs_loaded = false;
+
+BasicCredentialsManager::BasicCredentialsManager(const TCPSocketHandler* const socket_handler):
+    Botan::Credentials_Manager(),
+    socket_handler(socket_handler),
+    trusted_fingerprint{}
+{
+  BasicCredentialsManager::load_certs();
+}
+
+void BasicCredentialsManager::set_trusted_fingerprint(const std::string& fingerprint)
+{
+  this->trusted_fingerprint = fingerprint;
+}
+
+const std::string& BasicCredentialsManager::get_trusted_fingerprint() const
+{
+  return this->trusted_fingerprint;
+}
+
+void check_tls_certificate(const std::vector<Botan::X509_Certificate>& certs,
+                           const std::string& hostname, const std::string& trusted_fingerprint,
+                           std::exception_ptr exc)
+{
+
+  if (!trusted_fingerprint.empty() && !certs.empty() &&
+      trusted_fingerprint == certs[0].fingerprint() &&
+      certs[0].matches_dns_name(hostname))
+    // We trust the certificate, based on the trusted fingerprint and
+    // the fact that the hostname matches
+    return;
+
+  if (exc)
+    std::rethrow_exception(exc);
+}
+
+#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,34)
+void BasicCredentialsManager::verify_certificate_chain(const std::string& type,
+                                                       const std::string& purported_hostname,
+                                                       const std::vector<Botan::X509_Certificate>& certs)
+{
+  log_debug("Checking remote certificate (", type, ") for hostname ", purported_hostname);
+  try
+    {
+      Botan::Credentials_Manager::verify_certificate_chain(type, purported_hostname, certs);
+      log_debug("Certificate is valid");
+    }
+  catch (const std::exception& tls_exception)
+    {
+      log_warning("TLS certificate check failed: ", tls_exception.what());
+      std::exception_ptr exception_ptr{};
+      if (this->socket_handler->abort_on_invalid_cert())
+        exception_ptr = std::current_exception();
+
+      check_tls_certificate(certs, purported_hostname, this->trusted_fingerprint, exception_ptr);
+    }
+}
+#endif
+
+bool BasicCredentialsManager::try_to_open_one_ca_bundle(const std::vector<std::string>& paths)
+{
+  for (const auto& path: paths)
+    {
+      try
+        {
+          Botan::DataSource_Stream bundle(path);
+          log_debug("Using ca bundle: ", path);
+          while (!bundle.end_of_data() && bundle.check_available(27))
+            {
+              // TODO: remove this work-around for Botan 1.11.29
+              // https://github.com/randombit/botan/issues/438#issuecomment-192866796
+              // Note that every certificate that fails to be transcoded into latin-1
+              // will be ignored. As a result, some TLS connection may be refused
+              // because the certificate is signed by an issuer that was ignored.
+              try {
+                  Botan::X509_Certificate cert(bundle);
+                  BasicCredentialsManager::certificate_store.add_certificate(std::move(cert));
+                } catch (const Botan::Decoding_Error& error) {
+                  continue;
+                }
+            }
+          // Only use the first file that can successfully be read.
+          return true;
+        }
+      catch (const Botan::Stream_IO_Error& e)
+        {
+          log_debug(e.what());
+        }
+    }
+  return false;
+}
+
+void BasicCredentialsManager::load_certs()
+{
+  //  Only load the certificates the first time
+  if (BasicCredentialsManager::certs_loaded)
+    return;
+  const std::string conf_path = Config::get("ca_file", "");
+  std::vector<std::string> paths;
+  if (conf_path.empty())
+    paths = default_cert_files;
+  else
+    paths.push_back(conf_path);
+
+  if (BasicCredentialsManager::try_to_open_one_ca_bundle(paths))
+    BasicCredentialsManager::certs_loaded = true;
+  else
+    log_warning("The CA could not be loaded, TLS negociation will probably fail.");
+}
+
+std::vector<Botan::Certificate_Store*> BasicCredentialsManager::trusted_certificate_authorities(const std::string&, const std::string&)
+{
+  return {&this->certificate_store};
+}
+
+#endif
diff --git a/src/network/credentials_manager.hpp b/src/network/credentials_manager.hpp
new file mode 100644
index 0000000..c463ad4
--- /dev/null
+++ b/src/network/credentials_manager.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "biboumi.h"
+
+#ifdef BOTAN_FOUND
+
+#include <botan/botan.h>
+#include <botan/tls_client.h>
+
+class TCPSocketHandler;
+
+/**
+ * If the given cert isn’t valid, based on the given hostname
+ * and fingerprint, then throws the exception if it’s non-empty.
+ *
+ * Must be called after the standard (from Botan) way of
+ * checking the certificate, if we want to also accept certificates based
+ * on a trusted fingerprint.
+ */
+void check_tls_certificate(const std::vector<Botan::X509_Certificate>& certs,
+                           const std::string& hostname, const std::string& trusted_fingerprint,
+                           std::exception_ptr exc);
+
+class BasicCredentialsManager: public Botan::Credentials_Manager
+{
+public:
+  BasicCredentialsManager(const TCPSocketHandler* const socket_handler);
+
+  BasicCredentialsManager(BasicCredentialsManager&&) = delete;
+  BasicCredentialsManager(const BasicCredentialsManager&) = delete;
+  BasicCredentialsManager& operator=(const BasicCredentialsManager&) = delete;
+  BasicCredentialsManager& operator=(BasicCredentialsManager&&) = delete;
+
+#if BOTAN_VERSION_CODE < BOTAN_VERSION_CODE_FOR(1,11,34)
+  void verify_certificate_chain(const std::string& type,
+                                const std::string& purported_hostname,
+                                const std::vector<Botan::X509_Certificate>&) override final;
+#endif
+  std::vector<Botan::Certificate_Store*> trusted_certificate_authorities(const std::string& type,
+                                                                         const std::string& context) override final;
+  void set_trusted_fingerprint(const std::string& fingerprint);
+  const std::string& get_trusted_fingerprint() const;
+
+private:
+  const TCPSocketHandler* const socket_handler;
+
+  static bool try_to_open_one_ca_bundle(const std::vector<std::string>& paths);
+  static void load_certs();
+  static Botan::Certificate_Store_In_Memory certificate_store;
+  static bool certs_loaded;
+  std::string trusted_fingerprint;
+};
+
+#endif //BOTAN_FOUND
+
diff --git a/src/network/dns_handler.cpp b/src/network/dns_handler.cpp
new file mode 100644
index 0000000..7f0c96a
--- /dev/null
+++ b/src/network/dns_handler.cpp
@@ -0,0 +1,46 @@
+#include <biboumi.h>
+#ifdef UDNS_FOUND
+
+#include <network/dns_socket_handler.hpp>
+#include <network/dns_handler.hpp>
+#include <network/poller.hpp>
+
+#include <utils/timed_events.hpp>
+
+#include <udns.h>
+#include <cerrno>
+#include <cstring>
+
+class Resolver;
+
+using namespace std::string_literals;
+
+std::unique_ptr<DNSSocketHandler> DNSHandler::socket_handler{};
+
+DNSHandler::DNSHandler(std::shared_ptr<Poller>& poller)
+{
+  dns_init(nullptr, 0);
+  const auto socket = dns_open(nullptr);
+  if (socket == -1)
+    throw std::runtime_error("Failed to initialize udns socket: "s + strerror(errno));
+
+  DNSHandler::socket_handler = std::make_unique<DNSSocketHandler>(poller, socket);
+}
+
+void DNSHandler::destroy()
+{
+  DNSHandler::socket_handler.reset(nullptr);
+  dns_close(nullptr);
+}
+
+void DNSHandler::watch()
+{
+  DNSHandler::socket_handler->watch();
+}
+
+void DNSHandler::unwatch()
+{
+  DNSHandler::socket_handler->unwatch();
+}
+
+#endif /* UDNS_FOUND */
diff --git a/src/network/dns_handler.hpp b/src/network/dns_handler.hpp
new file mode 100644
index 0000000..c694452
--- /dev/null
+++ b/src/network/dns_handler.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include <biboumi.h>
+#ifdef UDNS_FOUND
+
+class Poller;
+
+#include <network/dns_socket_handler.hpp>
+
+#include <string>
+#include <vector>
+#include <memory>
+
+class DNSHandler
+{
+public:
+  explicit DNSHandler(std::shared_ptr<Poller>& poller);
+  ~DNSHandler() = default;
+
+  DNSHandler(const DNSHandler&) = delete;
+  DNSHandler(DNSHandler&&) = delete;
+  DNSHandler& operator=(const DNSHandler&) = delete;
+  DNSHandler& operator=(DNSHandler&&) = delete;
+
+  void destroy();
+
+  static void watch();
+  static void unwatch();
+
+private:
+  /**
+   * Manager for the socket returned by udns, that we need to watch with the poller
+   */
+  static std::unique_ptr<DNSSocketHandler> socket_handler;
+};
+
+#endif /* UDNS_FOUND */
diff --git a/src/network/dns_socket_handler.cpp b/src/network/dns_socket_handler.cpp
new file mode 100644
index 0000000..5c286c4
--- /dev/null
+++ b/src/network/dns_socket_handler.cpp
@@ -0,0 +1,43 @@
+#include <biboumi.h>
+#ifdef UDNS_FOUND
+
+#include <network/dns_socket_handler.hpp>
+#include <network/dns_handler.hpp>
+#include <network/poller.hpp>
+
+#include <udns.h>
+
+DNSSocketHandler::DNSSocketHandler(std::shared_ptr<Poller>& poller,
+                                   const socket_t socket):
+  SocketHandler(poller, socket)
+{
+  poller->add_socket_handler(this);
+}
+
+DNSSocketHandler::~DNSSocketHandler()
+{
+  this->unwatch();
+}
+
+void DNSSocketHandler::on_recv()
+{
+  dns_ioevent(nullptr, 0);
+}
+
+bool DNSSocketHandler::is_connected() const
+{
+  return true;
+}
+
+void DNSSocketHandler::unwatch()
+{
+  if (this->poller->is_managing_socket(this->socket))
+    this->poller->remove_socket_handler(this->socket);
+}
+
+void DNSSocketHandler::watch()
+{
+  this->poller->add_socket_handler(this);
+}
+
+#endif /* UDNS_FOUND */
diff --git a/src/network/dns_socket_handler.hpp b/src/network/dns_socket_handler.hpp
new file mode 100644
index 0000000..6e83e87
--- /dev/null
+++ b/src/network/dns_socket_handler.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <biboumi.h>
+#ifdef UDNS_FOUND
+
+#include <network/socket_handler.hpp>
+
+/**
+ * Manage the UDP socket provided by udns, we do not create, open or close the
+ * socket ourself: this is done by udns.  We only watch it for readability
+ */
+class DNSSocketHandler: public SocketHandler
+{
+public:
+  explicit DNSSocketHandler(std::shared_ptr<Poller>& poller, const socket_t socket);
+  ~DNSSocketHandler();
+  DNSSocketHandler(const DNSSocketHandler&) = delete;
+  DNSSocketHandler(DNSSocketHandler&&) = delete;
+  DNSSocketHandler& operator=(const DNSSocketHandler&) = delete;
+  DNSSocketHandler& operator=(DNSSocketHandler&&) = delete;
+
+  void on_recv() override final;
+
+  /**
+   * Always true, see the comment for connect()
+   */
+  bool is_connected() const override final;
+
+  void watch();
+  void unwatch();
+};
+
+#endif // UDNS_FOUND
diff --git a/src/network/poller.cpp b/src/network/poller.cpp
new file mode 100644
index 0000000..9f5bcfb
--- /dev/null
+++ b/src/network/poller.cpp
@@ -0,0 +1,234 @@
+#include <network/poller.hpp>
+#include <logger/logger.hpp>
+#include <utils/timed_events.hpp>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <cstring>
+#include <iostream>
+#include <stdexcept>
+
+Poller::Poller()
+{
+#if POLLER == POLL
+  this->nfds = 0;
+#elif POLLER == EPOLL
+  this->epfd = ::epoll_create1(0);
+  if (this->epfd == -1)
+    {
+      log_error("epoll failed: ", strerror(errno));
+      throw std::runtime_error("Could not create epoll instance");
+    }
+#endif
+}
+
+Poller::~Poller()
+{
+#if POLLER == EPOLL
+  if (this->epfd > 0)
+    ::close(this->epfd);
+#endif
+}
+
+void Poller::add_socket_handler(SocketHandler* socket_handler)
+{
+  // Don't do anything if the socket is already managed
+  const auto it = this->socket_handlers.find(socket_handler->get_socket());
+  if (it != this->socket_handlers.end())
+    return ;
+
+  this->socket_handlers.emplace(socket_handler->get_socket(), socket_handler);
+
+  // We always watch all sockets for receive events
+#if POLLER == POLL
+  this->fds[this->nfds].fd = socket_handler->get_socket();
+  this->fds[this->nfds].events = POLLIN;
+  this->nfds++;
+#endif
+#if POLLER == EPOLL
+  struct epoll_event event = {EPOLLIN, {socket_handler}};
+  const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_ADD, socket_handler->get_socket(), &event);
+  if (res == -1)
+    {
+      log_error("epoll_ctl failed: ", strerror(errno));
+      throw std::runtime_error("Could not add socket to epoll");
+    }
+#endif
+}
+
+void Poller::remove_socket_handler(const socket_t socket)
+{
+  const auto it = this->socket_handlers.find(socket);
+  if (it == this->socket_handlers.end())
+    throw std::runtime_error("Trying to remove a SocketHandler that is not managed");
+  this->socket_handlers.erase(it);
+
+#if POLLER == POLL
+  for (size_t i = 0; i < this->nfds; i++)
+    {
+      if (this->fds[i].fd == socket)
+        {
+          // Move all subsequent pollfd by one on the left, erasing the
+          // value of the one we remove
+          for (size_t j = i; j < this->nfds - 1; ++j)
+            {
+              this->fds[j].fd = this->fds[j+1].fd;
+              this->fds[j].events= this->fds[j+1].events;
+            }
+          this->nfds--;
+        }
+    }
+#elif POLLER == EPOLL
+  const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_DEL, socket, nullptr);
+  if (res == -1)
+    {
+      log_error("epoll_ctl failed: ", strerror(errno));
+      throw std::runtime_error("Could not remove socket from epoll");
+    }
+#endif
+}
+
+void Poller::watch_send_events(SocketHandler* socket_handler)
+{
+#if POLLER == POLL
+  for (size_t i = 0; i < this->nfds; ++i)
+    {
+      if (this->fds[i].fd == socket_handler->get_socket())
+        {
+          this->fds[i].events = POLLIN|POLLOUT;
+          return;
+        }
+    }
+  throw std::runtime_error("Cannot watch a non-registered socket for send events");
+#elif POLLER == EPOLL
+  struct epoll_event event = {EPOLLIN|EPOLLOUT, {socket_handler}};
+  const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event);
+  if (res == -1)
+    {
+      log_error("epoll_ctl failed: ", strerror(errno));
+      throw std::runtime_error("Could not modify socket flags in epoll");
+    }
+#endif
+}
+
+void Poller::stop_watching_send_events(SocketHandler* socket_handler)
+{
+#if POLLER == POLL
+  for (size_t i = 0; i <= this->nfds; ++i)
+    {
+      if (this->fds[i].fd == socket_handler->get_socket())
+        {
+          this->fds[i].events = POLLIN;
+          return;
+        }
+    }
+  throw std::runtime_error("Cannot watch a non-registered socket for send events");
+#elif POLLER == EPOLL
+  struct epoll_event event = {EPOLLIN, {socket_handler}};
+  const int res = ::epoll_ctl(this->epfd, EPOLL_CTL_MOD, socket_handler->get_socket(), &event);
+  if (res == -1)
+    {
+      log_error("epoll_ctl failed: ", strerror(errno));
+      throw std::runtime_error("Could not modify socket flags in epoll");
+    }
+#endif
+}
+
+int Poller::poll(const std::chrono::milliseconds& timeout)
+{
+  if (this->socket_handlers.empty() && timeout == utils::no_timeout)
+    return -1;
+#if POLLER == POLL
+  // Convert our nice timeout into this ugly struct
+  struct timespec timeout_ts;
+  struct timespec* timeout_tsp;
+  if (timeout > 0s)
+    {
+      auto seconds = std::chrono::duration_cast<std::chrono::seconds>(timeout);
+      timeout_ts.tv_sec = seconds.count();
+      timeout_ts.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - seconds).count();
+      timeout_tsp = &timeout_ts;
+    }
+  else
+    timeout_tsp = nullptr;
+
+  // Unblock all signals, only during the ppoll call
+  sigset_t empty_signal_set;
+  sigemptyset(&empty_signal_set);
+  int nb_events = ::ppoll(this->fds, this->nfds, timeout_tsp,
+                          &empty_signal_set);
+  if (nb_events < 0)
+    {
+      if (errno == EINTR)
+        return true;
+      log_error("poll failed: ", strerror(errno));
+      throw std::runtime_error("Poll failed");
+    }
+  // We cannot possibly have more ready events than the number of fds we are
+  // watching
+  assert(static_cast<unsigned int>(nb_events) <= this->nfds);
+  for (size_t i = 0; i < this->nfds && nb_events != 0; ++i)
+    {
+      auto socket_handler = this->socket_handlers.at(this->fds[i].fd);
+      if (this->fds[i].revents == 0)
+        continue;
+      else if (this->fds[i].revents & POLLIN && socket_handler->is_connected())
+        {
+          socket_handler->on_recv();
+          nb_events--;
+        }
+      else if (this->fds[i].revents & POLLOUT && socket_handler->is_connected())
+        {
+            socket_handler->on_send();
+            nb_events--;
+        }
+      else if (this->fds[i].revents & POLLOUT ||
+               this->fds[i].revents & POLLIN)
+        {
+          socket_handler->connect();
+          nb_events--;
+        }
+    }
+  return 1;
+#elif POLLER == EPOLL
+  static const size_t max_events = 12;
+  struct epoll_event revents[max_events];
+  // Unblock all signals, only during the epoll_pwait call
+  sigset_t empty_signal_set;
+  sigemptyset(&empty_signal_set);
+  const int nb_events = ::epoll_pwait(this->epfd, revents, max_events, timeout.count(),
+                                      &empty_signal_set);
+  if (nb_events == -1)
+    {
+      if (errno == EINTR)
+        return 0;
+      log_error("epoll wait: ", strerror(errno));
+      throw std::runtime_error("Epoll_wait failed");
+    }
+  for (int i = 0; i < nb_events; ++i)
+    {
+      auto socket_handler = static_cast<SocketHandler*>(revents[i].data.ptr);
+      if (revents[i].events & EPOLLIN && socket_handler->is_connected())
+        socket_handler->on_recv();
+      else if (revents[i].events & EPOLLOUT && socket_handler->is_connected())
+        socket_handler->on_send();
+      else if (revents[i].events & EPOLLOUT)
+        socket_handler->connect();
+    }
+  return nb_events;
+#endif
+}
+
+size_t Poller::size() const
+{
+  return this->socket_handlers.size();
+}
+
+bool Poller::is_managing_socket(const socket_t socket) const
+{
+  return (this->socket_handlers.find(socket) != this->socket_handlers.end());
+}
diff --git a/src/network/poller.hpp b/src/network/poller.hpp
new file mode 100644
index 0000000..3cc2710
--- /dev/null
+++ b/src/network/poller.hpp
@@ -0,0 +1,98 @@
+#pragma once
+
+
+#include <network/socket_handler.hpp>
+
+#include <unordered_map>
+#include <memory>
+#include <chrono>
+
+#define POLL 1
+#define EPOLL 2
+#define KQUEUE 3
+#include <biboumi.h>
+#ifndef POLLER
+ #define POLLER POLL
+#endif
+
+#if POLLER == POLL
+ #include <poll.h>
+ #define MAX_POLL_FD_NUMBER 4096
+#elif POLLER == EPOLL
+  #include <sys/epoll.h>
+#else
+  #error Invalid POLLER value
+#endif
+
+/**
+ * We pass some SocketHandlers to this Poller, which uses
+ * poll/epoll/kqueue/select etc to wait for events on these SocketHandlers,
+ * and call the callbacks when event occurs.
+ *
+ * TODO: support these pollers:
+ * - kqueue(2)
+ */
+
+class Poller
+{
+public:
+  explicit Poller();
+  ~Poller();
+  Poller(const Poller&) = delete;
+  Poller(Poller&&) = delete;
+  Poller& operator=(const Poller&) = delete;
+  Poller& operator=(Poller&&) = delete;
+  /**
+   * Add a SocketHandler to be monitored by this Poller. All receive events
+   * are always automatically watched.
+   */
+  void add_socket_handler(SocketHandler* socket_handler);
+  /**
+   * Remove (and stop managing) a SocketHandler, designated by the given socket_t.
+   */
+  void remove_socket_handler(const socket_t socket);
+  /**
+   * Signal the poller that he needs to watch for send events for the given
+   * SocketHandler.
+   */
+  void watch_send_events(SocketHandler* socket_handler);
+  /**
+   * Signal the poller that he needs to stop watching for send events for
+   * this SocketHandler.
+   */
+  void stop_watching_send_events(SocketHandler* socket_handler);
+  /**
+   * Wait for all watched events, and call the SocketHandlers' callbacks
+   * when one is ready.  Returns if nothing happened before the provided
+   * timeout.  If the timeout is 0, it waits forever.  If there is no
+   * watched event, returns -1 immediately, ignoring the timeout value.
+   * Otherwise, returns the number of event handled. If 0 is returned this
+   * means that we were interrupted by a signal, or the timeout occured.
+   */
+  int poll(const std::chrono::milliseconds& timeout);
+  /**
+   * Returns the number of SocketHandlers managed by the poller.
+   */
+  size_t size() const;
+  /**
+   * Whether the given socket is managed by the poller
+   */
+   bool is_managing_socket(const socket_t socket) const;
+
+private:
+  /**
+   * A "list" of all the SocketHandlers that we manage, indexed by socket,
+   * because that's what is returned by select/poll/etc when an event
+   * occures.
+   */
+  std::unordered_map<socket_t, SocketHandler*> socket_handlers;
+
+#if POLLER == POLL
+  struct pollfd fds[MAX_POLL_FD_NUMBER];
+  nfds_t nfds;
+#elif POLLER == EPOLL
+  int epfd;
+#endif
+};
+
+
diff --git a/src/network/resolver.cpp b/src/network/resolver.cpp
new file mode 100644
index 0000000..db7fb32
--- /dev/null
+++ b/src/network/resolver.cpp
@@ -0,0 +1,281 @@
+#include <network/dns_handler.hpp>
+#include <utils/timed_events.hpp>
+#include <network/resolver.hpp>
+#include <string.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#ifdef UDNS_FOUND
+# include <udns.h>
+#endif
+
+#include <fstream>
+#include <cstdlib>
+#include <sstream>
+#include <chrono>
+#include <map>
+
+using namespace std::string_literals;
+
+#ifdef UDNS_FOUND
+static std::map<int, std::string> dns_error_messages {
+    {DNS_E_TEMPFAIL, "Timeout while contacting DNS servers"},
+    {DNS_E_PROTOCOL, "Misformatted DNS reply"},
+    {DNS_E_NXDOMAIN, "Domain name not found"},
+    {DNS_E_NOMEM, "Out of memory"},
+    {DNS_E_BADQUERY, "Misformatted domain name"}
+};
+#endif
+
+Resolver::Resolver():
+#ifdef UDNS_FOUND
+  resolved4(false),
+  resolved6(false),
+  resolving(false),
+  port{},
+#endif
+  resolved(false),
+  error_msg{}
+{
+}
+
+void Resolver::resolve(const std::string& hostname, const std::string& port,
+                       SuccessCallbackType success_cb, ErrorCallbackType error_cb)
+{
+  this->error_cb = error_cb;
+  this->success_cb = success_cb;
+#ifdef UDNS_FOUND
+  this->port = port;
+#endif
+
+  this->start_resolving(hostname, port);
+}
+
+int Resolver::call_getaddrinfo(const char *name, const char* port, int flags)
+{
+  struct addrinfo hints;
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_flags = flags;
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = 0;
+
+  struct addrinfo* addr_res = nullptr;
+  const int res = ::getaddrinfo(name, port,
+                                &hints, &addr_res);
+
+  if (res == 0 && addr_res)
+    {
+      if (!this->addr)
+        this->addr.reset(addr_res);
+      else
+        { // Append this result at the end of the linked list
+          struct addrinfo *rp = this->addr.get();
+          while (rp->ai_next)
+            rp = rp->ai_next;
+          rp->ai_next = addr_res;
+        }
+    }
+
+  return res;
+}
+
+#ifdef UDNS_FOUND
+void Resolver::start_resolving(const std::string& hostname, const std::string& port)
+{
+  this->resolving = true;
+  this->resolved = false;
+  this->resolved4 = false;
+  this->resolved6 = false;
+
+  this->error_msg.clear();
+  this->addr.reset(nullptr);
+
+  // We first try to use it as an IP address directly. We tell getaddrinfo
+  // to NOT use any DNS resolution.
+  if (this->call_getaddrinfo(hostname.data(), port.data(), AI_NUMERICHOST) == 0)
+    {
+      this->on_resolved();
+      return;
+    }
+
+  // Then we look into /etc/hosts to translate the given hostname
+  const auto hosts = this->look_in_etc_hosts(hostname);
+  if (!hosts.empty())
+    {
+      for (const auto &host: hosts)
+        this->call_getaddrinfo(host.data(), port.data(), AI_NUMERICHOST);
+      this->on_resolved();
+      return;
+    }
+
+  // And finally, we try a DNS resolution
+  auto hostname6_resolved = [](dns_ctx*, dns_rr_a6* result, void* data)
+  {
+    Resolver* resolver = static_cast<Resolver*>(data);
+    resolver->on_hostname6_resolved(result);
+    resolver->after_resolved();
+    std::free(result);
+  };
+
+  auto hostname4_resolved = [](dns_ctx*, dns_rr_a4* result, void* data)
+  {
+    Resolver* resolver = static_cast<Resolver*>(data);
+    resolver->on_hostname4_resolved(result);
+    resolver->after_resolved();
+    std::free(result);
+  };
+
+  DNSHandler::watch();
+  auto res = dns_submit_a4(nullptr, hostname.data(), 0, hostname4_resolved, this);
+  if (!res)
+    this->on_hostname4_resolved(nullptr);
+  res = dns_submit_a6(nullptr, hostname.data(), 0, hostname6_resolved, this);
+  if (!res)
+    this->on_hostname6_resolved(nullptr);
+
+  this->start_timer();
+}
+
+void Resolver::start_timer()
+{
+  const auto timeout = dns_timeouts(nullptr, -1, 0);
+  if (timeout < 0)
+    return;
+  TimedEvent event(std::chrono::steady_clock::now() + std::chrono::seconds(timeout), [this]() { this->start_timer(); }, "DNS");
+  TimedEventsManager::instance().add_event(std::move(event));
+}
+
+std::vector<std::string> Resolver::look_in_etc_hosts(const std::string &hostname)
+{
+  std::ifstream hosts("/etc/hosts");
+  std::string line;
+
+  std::vector<std::string> results;
+  while (std::getline(hosts, line))
+    {
+      if (line.empty())
+        continue;
+
+      std::string ip;
+      std::istringstream line_stream(line);
+      line_stream >> ip;
+      if (ip.empty() || ip[0] == '#')
+        continue;
+
+      std::string host;
+      while (line_stream >> host && !host.empty() && host[0] != '#')
+        {
+          if (hostname == host)
+            {
+              results.push_back(ip);
+              break;
+            }
+        }
+    }
+  return results;
+}
+
+void Resolver::on_hostname4_resolved(dns_rr_a4 *result)
+{
+  this->resolved4 = true;
+
+  const auto status = dns_status(nullptr);
+
+  if (status >= 0 && result)
+    {
+      char buf[INET6_ADDRSTRLEN];
+
+      for (auto i = 0; i < result->dnsa4_nrr; ++i)
+        {
+          inet_ntop(AF_INET, &result->dnsa4_addr[i], buf, sizeof(buf));
+          this->call_getaddrinfo(buf, this->port.data(), AI_NUMERICHOST);
+        }
+    }
+  else
+    {
+      const auto error = dns_error_messages.find(status);
+      if (error != end(dns_error_messages))
+        this->error_msg = error->second;
+    }
+}
+
+void Resolver::on_hostname6_resolved(dns_rr_a6 *result)
+{
+  this->resolved6 = true;
+
+  const auto status = dns_status(nullptr);
+
+  if (status >= 0 && result)
+    {
+      char buf[INET6_ADDRSTRLEN];
+      for (auto i = 0; i < result->dnsa6_nrr; ++i)
+        {
+          inet_ntop(AF_INET6, &result->dnsa6_addr[i], buf, sizeof(buf));
+          this->call_getaddrinfo(buf, this->port.data(), AI_NUMERICHOST);
+        }
+    }
+}
+
+void Resolver::after_resolved()
+{
+  if (dns_active(nullptr) == 0)
+    DNSHandler::unwatch();
+
+  if (this->resolved6 && this->resolved4)
+    this->on_resolved();
+}
+
+void Resolver::on_resolved()
+{
+  this->resolved = true;
+  this->resolving = false;
+  if (!this->addr)
+    {
+      if (this->error_cb)
+        this->error_cb(this->error_msg.data());
+    }
+  else
+    {
+      if (this->success_cb)
+        this->success_cb(this->addr.get());
+    }
+}
+
+#else  // ifdef UDNS_FOUND
+
+void Resolver::start_resolving(const std::string& hostname, const std::string& port)
+{
+  // If the resolution fails, the addr will be unset
+  this->addr.reset(nullptr);
+
+  const auto res = this->call_getaddrinfo(hostname.data(), port.data(), 0);
+
+  this->resolved = true;
+
+  if (res != 0)
+    {
+      this->error_msg = gai_strerror(res);
+      if (this->error_cb)
+        this->error_cb(this->error_msg.data());
+    }
+  else
+    {
+      if (this->success_cb)
+        this->success_cb(this->addr.get());
+    }
+}
+#endif  // ifdef UDNS_FOUND
+
+std::string addr_to_string(const struct addrinfo* rp)
+{
+  char buf[INET6_ADDRSTRLEN];
+  if (rp->ai_family == AF_INET)
+    return ::inet_ntop(rp->ai_family,
+                       &reinterpret_cast<sockaddr_in*>(rp->ai_addr)->sin_addr,
+                       buf, sizeof(buf));
+  else if (rp->ai_family == AF_INET6)
+    return ::inet_ntop(rp->ai_family,
+                       &reinterpret_cast<sockaddr_in6*>(rp->ai_addr)->sin6_addr,
+                       buf, sizeof(buf));
+  return {};
+}
diff --git a/src/network/resolver.hpp b/src/network/resolver.hpp
new file mode 100644
index 0000000..f65ff86
--- /dev/null
+++ b/src/network/resolver.hpp
@@ -0,0 +1,122 @@
+#pragma once
+
+#include "biboumi.h"
+
+#include <functional>
+#include <vector>
+#include <memory>
+#include <string>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#ifdef UDNS_FOUND
+# include <udns.h>
+#endif
+
+class AddrinfoDeleter
+{
+ public:
+  void operator()(struct addrinfo* addr)
+  {
+    freeaddrinfo(addr);
+  }
+};
+
+
+class Resolver
+{
+public:
+
+  using ErrorCallbackType = std::function<void(const char*)>;
+  using SuccessCallbackType = std::function<void(const struct addrinfo*)>;
+
+  Resolver();
+  ~Resolver() = default;
+  Resolver(const Resolver&) = delete;
+  Resolver(Resolver&&) = delete;
+  Resolver& operator=(const Resolver&) = delete;
+  Resolver& operator=(Resolver&&) = delete;
+
+  bool is_resolving() const
+  {
+#ifdef UDNS_FOUND
+    return this->resolving;
+#else
+    return false;
+#endif
+  }
+
+  bool is_resolved() const
+  {
+    return this->resolved;
+  }
+
+  const auto& get_result() const
+  {
+    return this->addr;
+  }
+  std::string get_error_message() const
+  {
+    return this->error_msg;
+  }
+
+  void clear()
+  {
+#ifdef UDNS_FOUND
+    this->resolved6 = false;
+    this->resolved4 = false;
+    this->resolving = false;
+    this->port.clear();
+#endif
+    this->resolved = false;
+    this->addr.reset();
+    this->error_msg.clear();
+  }
+
+  void resolve(const std::string& hostname, const std::string& port,
+               SuccessCallbackType success_cb, ErrorCallbackType error_cb);
+
+private:
+  void start_resolving(const std::string& hostname, const std::string& port);
+  std::vector<std::string> look_in_etc_hosts(const std::string& hostname);
+  /**
+   * Call getaddrinfo() on the given hostname or IP, and append the result
+   * to our internal addrinfo list. Return getaddrinfo()’s return value.
+   */
+  int call_getaddrinfo(const char* name, const char* port, int flags);
+
+#ifdef UDNS_FOUND
+  void on_hostname4_resolved(dns_rr_a4 *result);
+  void on_hostname6_resolved(dns_rr_a6 *result);
+  /**
+   * Called after one record (4 or 6) has been resolved.
+   */
+  void after_resolved();
+
+  void start_timer();
+
+  void on_resolved();
+
+  bool resolved4;
+  bool resolved6;
+
+  bool resolving;
+
+  std::string port;
+
+#endif
+ /**
+  * Tells if we finished the resolution process. It doesn't indicate if it
+  * was successful (it is true even if the result is an error).
+  */
+  bool resolved;
+  std::string error_msg;
+
+  std::unique_ptr<struct addrinfo, AddrinfoDeleter> addr;
+
+  ErrorCallbackType error_cb;
+  SuccessCallbackType success_cb;
+};
+
+std::string addr_to_string(const struct addrinfo* rp);
diff --git a/src/network/socket_handler.hpp b/src/network/socket_handler.hpp
new file mode 100644
index 0000000..181a6c0
--- /dev/null
+++ b/src/network/socket_handler.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include <biboumi.h>
+#include <memory>
+
+class Poller;
+
+using socket_t = int;
+
+class SocketHandler
+{
+public:
+  explicit SocketHandler(std::shared_ptr<Poller>& poller, const socket_t socket):
+    poller(poller),
+    socket(socket)
+  {}
+  virtual ~SocketHandler() = default;
+  SocketHandler(const SocketHandler&) = delete;
+  SocketHandler(SocketHandler&&) = delete;
+  SocketHandler& operator=(const SocketHandler&) = delete;
+  SocketHandler& operator=(SocketHandler&&) = delete;
+
+  virtual void on_recv() {}
+  virtual void on_send() {}
+  virtual void connect() {}
+  virtual bool is_connected() const = 0;
+
+  socket_t get_socket() const
+  { return this->socket; }
+
+protected:
+  /**
+   * A pointer to the poller that manages us, because we need to communicate
+   * with it.
+   */
+  std::shared_ptr<Poller> poller;
+  /**
+   * The handled socket.
+   */
+  socket_t socket;
+};
+
diff --git a/src/network/tcp_client_socket_handler.cpp b/src/network/tcp_client_socket_handler.cpp
new file mode 100644
index 0000000..4628703
--- /dev/null
+++ b/src/network/tcp_client_socket_handler.cpp
@@ -0,0 +1,261 @@
+#include <network/tcp_client_socket_handler.hpp>
+#include <utils/timed_events.hpp>
+#include <utils/scopeguard.hpp>
+#include <network/poller.hpp>
+
+#include <logger/logger.hpp>
+
+#include <cstring>
+#include <unistd.h>
+#include <fcntl.h>
+
+using namespace std::string_literals;
+
+TCPClientSocketHandler::TCPClientSocketHandler(std::shared_ptr<Poller>& poller):
+   TCPSocketHandler(poller),
+   hostname_resolution_failed(false),
+   connected(false),
+   connecting(false)
+{}
+
+TCPClientSocketHandler::~TCPClientSocketHandler()
+{
+  this->close();
+}
+
+void TCPClientSocketHandler::init_socket(const struct addrinfo* rp)
+{
+  if (this->socket != -1)
+    ::close(this->socket);
+  if ((this->socket = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) == -1)
+    throw std::runtime_error("Could not create socket: "s + std::strerror(errno));
+  // Bind the socket to a specific address, if specified
+  if (!this->bind_addr.empty())
+    {
+      // Convert the address from string format to a sockaddr that can be
+      // used in bind()
+      struct addrinfo* result;
+      struct addrinfo hints;
+      memset(&hints, 0, sizeof(hints));
+      hints.ai_flags = AI_NUMERICHOST;
+      hints.ai_family = AF_UNSPEC;
+      int err = ::getaddrinfo(this->bind_addr.data(), nullptr, &hints, &result);
+      if (err != 0 || !result)
+        log_error("Failed to bind socket to ", this->bind_addr, ": ",
+                  gai_strerror(err));
+      else
+        {
+          utils::ScopeGuard sg([result](){ freeaddrinfo(result); });
+          struct addrinfo* rp;
+          for (rp = result; rp; rp = rp->ai_next)
+            {
+              if ((::bind(this->socket,
+                         reinterpret_cast<const struct sockaddr*>(rp->ai_addr),
+                         rp->ai_addrlen)) == 0)
+                break;
+            }
+          if (!rp)
+            log_error("Failed to bind socket to ", this->bind_addr, ": ",
+                      strerror(errno));
+          else
+            log_info("Socket successfully bound to ", this->bind_addr);
+        }
+    }
+  int optval = 1;
+  if (::setsockopt(this->socket, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1)
+    log_warning("Failed to enable TCP keepalive on socket: ", strerror(errno));
+  // Set the socket on non-blocking mode.  This is useful to receive a EAGAIN
+  // error when connect() would block, to not block the whole process if a
+  // remote is not responsive.
+  const int existing_flags = ::fcntl(this->socket, F_GETFL, 0);
+  if ((existing_flags == -1) ||
+      (::fcntl(this->socket, F_SETFL, existing_flags | O_NONBLOCK) == -1))
+    throw std::runtime_error("Could not initialize socket: "s + std::strerror(errno));
+}
+
+void TCPClientSocketHandler::connect(const std::string& address, const std::string& port, const bool tls)
+{
+  this->address = address;
+  this->port = port;
+  this->use_tls = tls;
+
+  struct addrinfo* addr_res;
+
+  if (!this->connecting)
+    {
+      // Get the addrinfo from getaddrinfo (or using udns), only if
+      // this is the first call of this function.
+      if (!this->resolver.is_resolved())
+        {
+          log_info("Trying to connect to ", address, ":", port);
+          // Start the asynchronous process of resolving the hostname. Once
+          // the addresses have been found and `resolved` has been set to true
+          // (but connecting will still be false), TCPClientSocketHandler::connect()
+          // needs to be called, again.
+          this->resolver.resolve(address, port,
+                                 [this](const struct addrinfo*)
+                                 {
+                                   log_debug("Resolution success, calling connect() again");
+                                   this->connect();
+                                 },
+                                 [this](const char*)
+                                 {
+                                   log_debug("Resolution failed, calling connect() again");
+                                   this->connect();
+                                 });
+          return;
+        }
+      else
+        {
+          // The DNS resolver resolved the hostname and the available addresses
+          // where saved in the addrinfo linked list. Now, just use
+          // this list to try to connect.
+          addr_res = this->resolver.get_result().get();
+          if (!addr_res)
+            {
+              this->hostname_resolution_failed = true;
+              const auto msg = this->resolver.get_error_message();
+              this->close();
+              this->on_connection_failed(msg);
+              return ;
+            }
+        }
+    }
+  else
+    { // This function is called again, use the saved addrinfo structure,
+      // instead of re-doing the whole getaddrinfo process.
+      addr_res = &this->addrinfo;
+    }
+
+  for (struct addrinfo* rp = addr_res; rp; rp = rp->ai_next)
+    {
+      if (!this->connecting)
+        {
+          try {
+            this->init_socket(rp);
+          }
+          catch (const std::runtime_error& error) {
+            log_error("Failed to init socket: ", error.what());
+            break;
+          }
+        }
+
+      this->display_resolved_ip(rp);
+
+      if (::connect(this->socket, rp->ai_addr, rp->ai_addrlen) == 0
+          || errno == EISCONN)
+        {
+          log_info("Connection success.");
+          TimedEventsManager::instance().cancel("connection_timeout"s +
+                                                std::to_string(this->socket));
+          this->poller->add_socket_handler(this);
+          this->connected = true;
+          this->connecting = false;
+#ifdef BOTAN_FOUND
+          if (this->use_tls)
+            this->start_tls(this->address, this->port);
+#endif
+          this->connection_date = std::chrono::system_clock::now();
+
+          // Get our local TCP port and store it
+          this->local_port = static_cast<uint16_t>(-1);
+          if (rp->ai_family == AF_INET6)
+            {
+              struct sockaddr_in6 a;
+              socklen_t l = sizeof(a);
+              if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1)
+                this->local_port = ntohs(a.sin6_port);
+            }
+          else if (rp->ai_family == AF_INET)
+            {
+              struct sockaddr_in a;
+              socklen_t l = sizeof(a);
+              if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1)
+                this->local_port = ntohs(a.sin_port);
+            }
+
+          log_debug("Local port: ", this->local_port, ", and remote port: ", this->port);
+
+          this->on_connected();
+          return ;
+        }
+      else if (errno == EINPROGRESS || errno == EALREADY)
+        {   // retry this process later, when the socket
+            // is ready to be written on.
+          this->connecting = true;
+          this->poller->add_socket_handler(this);
+          this->poller->watch_send_events(this);
+          // Save the addrinfo structure, to use it on the next call
+          this->ai_addrlen = rp->ai_addrlen;
+          memcpy(&this->ai_addr, rp->ai_addr, this->ai_addrlen);
+          memcpy(&this->addrinfo, rp, sizeof(struct addrinfo));
+          this->addrinfo.ai_addr = reinterpret_cast<struct sockaddr*>(&this->ai_addr);
+          this->addrinfo.ai_next = nullptr;
+          // If the connection has not succeeded or failed in 5s, we consider
+          // it to have failed
+          TimedEventsManager::instance().add_event(
+                                                   TimedEvent(std::chrono::steady_clock::now() + 5s,
+                                                              std::bind(&TCPClientSocketHandler::on_connection_timeout, this),
+                                                              "connection_timeout"s + std::to_string(this->socket)));
+          return ;
+        }
+      log_info("Connection failed:", std::strerror(errno));
+    }
+  log_error("All connection attempts failed.");
+  this->close();
+  this->on_connection_failed(std::strerror(errno));
+  return ;
+}
+
+void TCPClientSocketHandler::on_connection_timeout()
+{
+  this->close();
+  this->on_connection_failed("connection timed out");
+}
+
+void TCPClientSocketHandler::connect()
+{
+  this->connect(this->address, this->port, this->use_tls);
+}
+
+void TCPClientSocketHandler::close()
+{
+  TimedEventsManager::instance().cancel("connection_timeout"s +
+                                        std::to_string(this->socket));
+
+  TCPSocketHandler::close();
+
+  this->connected = false;
+  this->connecting = false;
+  this->port.clear();
+  this->resolver.clear();
+}
+
+void TCPClientSocketHandler::display_resolved_ip(struct addrinfo* rp) const
+{
+  if (rp->ai_family == AF_INET)
+    log_debug("Trying IPv4 address ", addr_to_string(rp));
+  else if (rp->ai_family == AF_INET6)
+    log_debug("Trying IPv6 address ", addr_to_string(rp));
+}
+
+bool TCPClientSocketHandler::is_connected() const
+{
+  return this->connected;
+}
+
+bool TCPClientSocketHandler::is_connecting() const
+{
+  return this->connecting || this->resolver.is_resolving();
+}
+
+std::string TCPClientSocketHandler::get_port() const
+{
+  return this->port;
+}
+
+bool TCPClientSocketHandler::match_port_pairt(const uint16_t local, const uint16_t remote) const
+{
+  const uint16_t remote_port = static_cast<uint16_t>(std::stoi(this->port));
+  return this->is_connected() && local == this->local_port && remote == remote_port;
+}
diff --git a/src/network/tcp_client_socket_handler.hpp b/src/network/tcp_client_socket_handler.hpp
new file mode 100644
index 0000000..74caca9
--- /dev/null
+++ b/src/network/tcp_client_socket_handler.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#include <network/tcp_socket_handler.hpp>
+
+class TCPClientSocketHandler: public TCPSocketHandler
+{
+ public:
+  TCPClientSocketHandler(std::shared_ptr<Poller>& poller);
+  ~TCPClientSocketHandler();
+  /**
+   * Connect to the remote server, and call on_connected() if this
+   * succeeds. If tls is true, we set use_tls to true and will also call
+   * start_tls() when the connection succeeds.
+   */
+  void connect(const std::string& address, const std::string& port, const bool tls);
+  void connect() override final;
+  /**
+   * Called by a TimedEvent, when the connection did not succeed or fail
+   * after a given time.
+   */
+  void on_connection_timeout();
+  /**
+   * Called when the connection is successful.
+   */
+  virtual void on_connected() = 0;
+  bool is_connected() const override;
+  bool is_connecting() const override;
+
+  std::string get_port() const;
+
+  void close() override final;
+  std::chrono::system_clock::time_point connection_date;
+
+  /**
+   * Whether or not this connection is using the two given TCP ports.
+   */
+  bool match_port_pairt(const uint16_t local, const uint16_t remote) const;
+
+ protected:
+  bool hostname_resolution_failed;
+  /**
+   * Address to bind the socket to, before calling connect().
+   * If empty, it’s equivalent to binding to INADDR_ANY.
+   */
+  std::string bind_addr;
+  /**
+   * Display the resolved IP, just for information purpose.
+   */
+  void display_resolved_ip(struct addrinfo* rp) const;
+ private:
+  /**
+   * Initialize the socket with the parameters contained in the given
+   * addrinfo structure.
+   */
+  void init_socket(const struct addrinfo* rp);
+  /**
+   * DNS resolver
+   */
+  Resolver resolver;
+  /**
+   * Keep the details of the addrinfo returned by the resolver that
+   * triggered a EINPROGRESS error when connect()ing to it, to reuse it
+   * directly when connect() is called again.
+   */
+  struct addrinfo addrinfo{};
+  struct sockaddr_in6 ai_addr{};
+  socklen_t ai_addrlen{};
+
+  /**
+   * Hostname we are connected/connecting to
+   */
+  std::string address;
+  /**
+   * Port we are connected/connecting to
+   */
+  std::string port;
+
+  uint16_t local_port{};
+
+  bool connected;
+  bool connecting;
+};
diff --git a/src/network/tcp_server_socket.hpp b/src/network/tcp_server_socket.hpp
new file mode 100644
index 0000000..c511962
--- /dev/null
+++ b/src/network/tcp_server_socket.hpp
@@ -0,0 +1,70 @@
+#pragma once
+
+#include <network/socket_handler.hpp>
+#include <network/poller.hpp>
+#include <logger/logger.hpp>
+
+#include <string>
+
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+
+#include <cstring>
+#include <cassert>
+
+template <typename RemoteSocketType>
+class TcpSocketServer: public SocketHandler
+{
+ public:
+  TcpSocketServer(std::shared_ptr<Poller>& poller, const uint16_t port):
+      SocketHandler(poller, -1)
+  {
+    if ((this->socket = ::socket(AF_INET6, SOCK_STREAM, 0)) == -1)
+      throw std::runtime_error(std::string{"Could not create socket: "} + std::strerror(errno));
+
+    int opt = 1;
+    if (::setsockopt(this->socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1)
+      throw std::runtime_error(std::string{"Failed to set socket option: "} + std::strerror(errno));
+
+    struct sockaddr_in6 addr{};
+    addr.sin6_family = AF_INET6;
+    addr.sin6_port = htons(port);
+    addr.sin6_addr = IN6ADDR_ANY_INIT;
+    if ((::bind(this->socket, (const struct sockaddr*)&addr, sizeof(addr))) == -1)
+      { // If we can’t listen on this port, we just give up, but this is not fatal.
+        log_warning("Failed to bind on port ", std::to_string(port), ": ", std::strerror(errno));
+        return;
+      }
+
+    if ((::listen(this->socket, 10)) == -1)
+      throw std::runtime_error("listen() failed");
+
+    this->accept();
+  }
+  ~TcpSocketServer() = default;
+
+  void on_recv() override
+  {
+    // Accept a RemoteSocketType
+    int socket = ::accept(this->socket, nullptr, nullptr);
+
+    auto client = std::make_unique<RemoteSocketType>(poller, socket, *this);
+    this->poller->add_socket_handler(client.get());
+    this->sockets.push_back(std::move(client));
+  }
+
+ protected:
+  std::vector<std::unique_ptr<RemoteSocketType>> sockets;
+
+ private:
+  void accept()
+  {
+    this->poller->add_socket_handler(this);
+  }
+  bool is_connected() const override
+  {
+    return true;
+  }
+};
diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp
new file mode 100644
index 0000000..7eebae0
--- /dev/null
+++ b/src/network/tcp_socket_handler.cpp
@@ -0,0 +1,358 @@
+#include <network/tcp_socket_handler.hpp>
+#include <network/dns_handler.hpp>
+
+#include <network/poller.hpp>
+
+#include <logger/logger.hpp>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <stdexcept>
+#include <unistd.h>
+#include <errno.h>
+#include <cstring>
+
+#ifdef BOTAN_FOUND
+# include <botan/hex.h>
+# include <botan/tls_exceptn.h>
+
+namespace
+{
+    Botan::AutoSeeded_RNG& get_rng()
+    {
+      static Botan::AutoSeeded_RNG rng{};
+      return rng;
+    }
+    BiboumiTLSPolicy& get_policy()
+    {
+      static BiboumiTLSPolicy policy{};
+      return policy;
+    }
+    Botan::TLS::Session_Manager_In_Memory& get_session_manager()
+    {
+      static Botan::TLS::Session_Manager_In_Memory session_manager{get_rng()};
+      return session_manager;
+    }
+}
+#endif
+
+#ifndef UIO_FASTIOV
+# define UIO_FASTIOV 8
+#endif
+
+using namespace std::string_literals;
+using namespace std::chrono_literals;
+
+namespace ph = std::placeholders;
+
+TCPSocketHandler::TCPSocketHandler(std::shared_ptr<Poller>& poller):
+  SocketHandler(poller, -1),
+  use_tls(false)
+#ifdef BOTAN_FOUND
+  ,credential_manager(this)
+#endif
+{}
+
+TCPSocketHandler::~TCPSocketHandler()
+{
+  if (this->poller->is_managing_socket(this->get_socket()))
+    this->poller->remove_socket_handler(this->get_socket());
+  if (this->socket != -1)
+    {
+      ::close(this->socket);
+      this->socket = -1;
+    }
+}
+
+void TCPSocketHandler::on_recv()
+{
+#ifdef BOTAN_FOUND
+  if (this->use_tls)
+    this->tls_recv();
+  else
+#endif
+    this->plain_recv();
+}
+
+void TCPSocketHandler::plain_recv()
+{
+  static constexpr size_t buf_size = 4096;
+  char buf[buf_size];
+  void* recv_buf = this->get_receive_buffer(buf_size);
+
+  if (recv_buf == nullptr)
+    recv_buf = buf;
+
+  const ssize_t size = this->do_recv(recv_buf, buf_size);
+
+  if (size > 0)
+    {
+      if (buf == recv_buf)
+        {
+          // data needs to be placed in the in_buf string, because no buffer
+          // was provided to receive that data directly. The in_buf buffer
+          // will be handled in parse_in_buffer()
+          this->in_buf += std::string(buf, size);
+        }
+      this->parse_in_buffer(size);
+    }
+}
+
+ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size)
+{
+  ssize_t size = ::recv(this->socket, recv_buf, buf_size, 0);
+  if (0 == size)
+    {
+      this->on_connection_close("");
+      this->close();
+    }
+  else if (-1 == size)
+    {
+      if (this->is_connecting())
+        log_warning("Error connecting: ", strerror(errno));
+      else
+        log_warning("Error while reading from socket: ", strerror(errno));
+      // Remember if we were connecting, or already connected when this
+      // happened, because close() sets this->connecting to false
+      const auto were_connecting = this->is_connecting();
+      this->close();
+      if (were_connecting)
+        this->on_connection_failed(strerror(errno));
+      else
+        this->on_connection_close(strerror(errno));
+    }
+  return size;
+}
+
+void TCPSocketHandler::on_send()
+{
+  struct iovec msg_iov[UIO_FASTIOV] = {};
+  struct msghdr msg{nullptr, 0,
+      msg_iov,
+      0, nullptr, 0, 0};
+  for (const std::string& s: this->out_buf)
+    {
+      // unconsting the content of s is ok, sendmsg will never modify it
+      msg_iov[msg.msg_iovlen].iov_base = const_cast<char*>(s.data());
+      msg_iov[msg.msg_iovlen].iov_len = s.size();
+      msg.msg_iovlen++;
+      if (msg.msg_iovlen == UIO_FASTIOV)
+        break;
+    }
+  ssize_t res = ::sendmsg(this->socket, &msg, MSG_NOSIGNAL);
+  if (res < 0)
+    {
+      log_error("sendmsg failed: ", strerror(errno));
+      this->on_connection_close(strerror(errno));
+      this->close();
+    }
+  else
+    {
+      // remove all the strings that were successfully sent.
+      auto it = this->out_buf.begin();
+      while (it != this->out_buf.end())
+        {
+          if (static_cast<size_t>(res) >= it->size())
+            {
+              res -= it->size();
+              ++it;
+            }
+          else
+            {
+              // If one string has partially been sent, we use substr to
+              // crop it
+              if (res > 0)
+                *it = it->substr(res, std::string::npos);
+              break;
+            }
+        }
+      this->out_buf.erase(this->out_buf.begin(), it);
+      if (this->out_buf.empty())
+        this->poller->stop_watching_send_events(this);
+    }
+}
+
+void TCPSocketHandler::close()
+{
+  if (this->is_connected() || this->is_connecting())
+    this->poller->remove_socket_handler(this->get_socket());
+  if (this->socket != -1)
+    {
+      ::close(this->socket);
+      this->socket = -1;
+    }
+  this->in_buf.clear();
+  this->out_buf.clear();
+}
+
+void TCPSocketHandler::send_data(std::string&& data)
+{
+#ifdef BOTAN_FOUND
+  if (this->use_tls)
+    try {
+      this->tls_send(std::move(data));
+    } catch (const Botan::TLS::TLS_Exception& e) {
+      this->on_connection_close("TLS error: "s + e.what());
+      this->close();
+      return ;
+    }
+  else
+#endif
+    this->raw_send(std::move(data));
+}
+
+void TCPSocketHandler::raw_send(std::string&& data)
+{
+  if (data.empty())
+    return ;
+  this->out_buf.emplace_back(std::move(data));
+  if (this->is_connected())
+    this->poller->watch_send_events(this);
+}
+
+void TCPSocketHandler::send_pending_data()
+{
+  if (this->is_connected() && !this->out_buf.empty())
+    this->poller->watch_send_events(this);
+}
+
+bool TCPSocketHandler::is_using_tls() const
+{
+  return this->use_tls;
+}
+
+void* TCPSocketHandler::get_receive_buffer(const size_t) const
+{
+  return nullptr;
+}
+
+void TCPSocketHandler::consume_in_buffer(const std::size_t size)
+{
+  this->in_buf = this->in_buf.substr(size, std::string::npos);
+}
+
+#ifdef BOTAN_FOUND
+void TCPSocketHandler::start_tls(const std::string& address, const std::string& port)
+{
+  Botan::TLS::Server_Information server_info(address, "irc", std::stoul(port));
+  this->tls = std::make_unique<Botan::TLS::Client>(
+# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32)
+      *this,
+# else
+      [this](const Botan::byte* data, size_t size) { this->tls_emit_data(data, size); },
+      [this](const Botan::byte* data, size_t size) { this->tls_record_received(0, data, size); },
+      [this](Botan::TLS::Alert alert, const Botan::byte*, size_t) { this->tls_alert(alert); },
+      [this](const Botan::TLS::Session& session) { return this->tls_session_established(session); },
+# endif
+      get_session_manager(), this->credential_manager, get_policy(),
+      get_rng(), server_info, Botan::TLS::Protocol_Version::latest_tls_version());
+}
+
+void TCPSocketHandler::tls_recv()
+{
+  static constexpr size_t buf_size = 4096;
+  Botan::byte recv_buf[buf_size];
+
+  const ssize_t size = this->do_recv(recv_buf, buf_size);
+  if (size > 0)
+    {
+      const bool was_active = this->tls->is_active();
+      try {
+        this->tls->received_data(recv_buf, static_cast<size_t>(size));
+      } catch (const Botan::TLS::TLS_Exception& e) {
+        // May happen if the server sends malformed TLS data (buggy server,
+        // or more probably we are just connected to a server that sends
+        // plain-text)
+        this->on_connection_close("TLS error: "s + e.what());
+        this->close();
+        return ;
+      }
+      if (!was_active && this->tls->is_active())
+        this->on_tls_activated();
+    }
+}
+
+void TCPSocketHandler::tls_send(std::string&& data)
+{
+  // We may not be connected yet, or the tls session has
+  // not yet been negociated
+  if (this->tls && this->tls->is_active())
+    {
+      const bool was_active = this->tls->is_active();
+      if (!this->pre_buf.empty())
+        {
+          this->tls->send(this->pre_buf.data(), this->pre_buf.size());
+          this->pre_buf.clear();
+        }
+      if (!data.empty())
+        this->tls->send(reinterpret_cast<const Botan::byte*>(data.data()),
+                       data.size());
+      if (!was_active && this->tls->is_active())
+        this->on_tls_activated();
+    }
+  else
+    this->pre_buf.insert(this->pre_buf.end(),
+                         std::make_move_iterator(data.begin()),
+                         std::make_move_iterator(data.end()));
+}
+
+void TCPSocketHandler::tls_record_received(uint64_t, const Botan::byte *data, size_t size)
+{
+  this->in_buf += std::string(reinterpret_cast<const char*>(data),
+                              size);
+  if (!this->in_buf.empty())
+    this->parse_in_buffer(size);
+}
+
+void TCPSocketHandler::tls_emit_data(const Botan::byte *data, size_t size)
+{
+  this->raw_send(std::string(reinterpret_cast<const char*>(data), size));
+}
+
+void TCPSocketHandler::tls_alert(Botan::TLS::Alert alert)
+{
+  log_debug("tls_alert: ", alert.type_string());
+}
+
+bool TCPSocketHandler::tls_session_established(const Botan::TLS::Session& session)
+{
+  log_debug("Handshake with ", session.server_info().hostname(), " complete.",
+            " Version: ", session.version().to_string(),
+            " using ", session.ciphersuite().to_string());
+  if (!session.session_id().empty())
+    log_debug("Session ID ", Botan::hex_encode(session.session_id()));
+  if (!session.session_ticket().empty())
+    log_debug("Session ticket ", Botan::hex_encode(session.session_ticket()));
+  return true;
+}
+
+#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,34)
+void TCPSocketHandler::tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& cert_chain,
+                                             const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
+                                             const std::vector<Botan::Certificate_Store*>& trusted_roots,
+                                             Botan::Usage_Type usage, const std::string& hostname,
+                                             const Botan::TLS::Policy& policy)
+{
+  log_debug("Checking remote certificate for hostname ", hostname);
+  try
+    {
+      Botan::TLS::Callbacks::tls_verify_cert_chain(cert_chain, ocsp_responses, trusted_roots, usage, hostname, policy);
+      log_debug("Certificate is valid");
+    }
+  catch (const std::exception& tls_exception)
+    {
+      log_warning("TLS certificate check failed: ", tls_exception.what());
+      std::exception_ptr exception_ptr{};
+      if (this->abort_on_invalid_cert())
+        exception_ptr = std::current_exception();
+
+      check_tls_certificate(cert_chain, hostname, this->credential_manager.get_trusted_fingerprint(), exception_ptr);
+    }
+}
+#endif
+
+void TCPSocketHandler::on_tls_activated()
+{
+  this->send_data({});
+}
+
+#endif // BOTAN_FOUND
diff --git a/src/network/tcp_socket_handler.hpp b/src/network/tcp_socket_handler.hpp
new file mode 100644
index 0000000..ba23861
--- /dev/null
+++ b/src/network/tcp_socket_handler.hpp
@@ -0,0 +1,251 @@
+#pragma once
+
+#include "biboumi.h"
+
+#include <network/socket_handler.hpp>
+#include <network/resolver.hpp>
+
+#include <network/credentials_manager.hpp>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <chrono>
+#include <vector>
+#include <memory>
+#include <string>
+#include <list>
+
+#ifdef BOTAN_FOUND
+
+# include <botan/types.h>
+# include <botan/botan.h>
+# include <botan/tls_session_manager.h>
+
+class BiboumiTLSPolicy: public Botan::TLS::Policy
+{
+public:
+# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,33)
+  bool use_ecc_point_compression() const override
+  {
+    return true;
+  }
+  bool require_cert_revocation_info() const override
+  {
+    return false;
+  }
+# endif
+};
+
+# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32)
+#  define BOTAN_TLS_CALLBACKS_OVERRIDE override final
+# else
+#  define BOTAN_TLS_CALLBACKS_OVERRIDE
+# endif
+#endif
+
+/**
+ * Does all the read/write, buffering etc. With optional tls.
+ * But doesn’t do any connect() or accept() or anything else.
+ */
+class TCPSocketHandler: public SocketHandler
+#ifdef BOTAN_FOUND
+# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32)
+    ,public Botan::TLS::Callbacks
+# endif
+#endif
+{
+protected:
+  ~TCPSocketHandler();
+public:
+  explicit TCPSocketHandler(std::shared_ptr<Poller>& poller);
+  TCPSocketHandler(const TCPSocketHandler&) = delete;
+  TCPSocketHandler(TCPSocketHandler&&) = delete;
+  TCPSocketHandler& operator=(const TCPSocketHandler&) = delete;
+  TCPSocketHandler& operator=(TCPSocketHandler&&) = delete;
+
+  /**
+   * Reads raw data from the socket. And pass it to parse_in_buffer()
+   * If we are using TLS on this connection, we call tls_recv()
+   */
+  void on_recv() override final;
+  /**
+   * Write as much data from out_buf as possible, in the socket.
+   */
+  void on_send() override final;
+  /**
+   * Add the given data to out_buf and tell our poller that we want to be
+   * notified when a send event is ready.
+   *
+   * This can be overriden if we want to modify the data before sending
+   * it. For example if we want to encrypt it.
+   */
+  void send_data(std::string&& data);
+  /**
+   * Watch the socket for send events, if our out buffer is not empty.
+   */
+  void send_pending_data();
+  /**
+   * Close the connection, remove us from the poller
+   */
+  virtual void close();
+  /**
+   * Handle/consume (some of) the data received so far.  The data to handle
+   * may be in the in_buf buffer, or somewhere else, depending on what
+   * get_receive_buffer() returned.  If some data is used from in_buf, it
+   * should be truncated, only the unused data should be left untouched.
+   *
+   * The size argument is the size of the last chunk of data that was added to the buffer.
+   *
+   * The function should call consume_in_buffer, with the size that was consumed by the
+   * “parsing”, and thus to be removed from the input buffer.
+   */
+  virtual void parse_in_buffer(const size_t size) = 0;
+#ifdef BOTAN_FOUND
+  /**
+   * Tell whether the credential manager should cancel the connection when the
+   * certificate is invalid.
+   */
+  virtual bool abort_on_invalid_cert() const
+  {
+    return true;
+  }
+#endif
+  bool is_using_tls() const;
+
+private:
+  /**
+   * Reads from the socket into the provided buffer.  If an error occurs
+   * (read returns <= 0), the handling of the error is done here (close the
+   * connection, log a message, etc).
+   *
+   * Returns the value returned by ::recv(), so the buffer should not be
+   * used if it’s not positive.
+   */
+  ssize_t do_recv(void* recv_buf, const size_t buf_size);
+  /**
+   * Reads data from the socket and calls parse_in_buffer with it.
+   */
+  void plain_recv();
+  /**
+   * Mark the given data as ready to be sent, as-is, on the socket, as soon
+   * as we can.
+   */
+  void raw_send(std::string&& data);
+
+ protected:
+  virtual bool is_connecting() const = 0;
+#ifdef BOTAN_FOUND
+  /**
+   * Create the TLS::Client object, with all the callbacks etc. This must be
+   * called only when we know we are able to send TLS-encrypted data over
+   * the socket.
+   */
+  void start_tls(const std::string& address, const std::string& port);
+ private:
+  /**
+   * An additional step to pass the data into our tls object to decrypt it
+   * before passing it to parse_in_buffer.
+   */
+  void tls_recv();
+  /**
+   * Pass the data to the tls object in order to encrypt it. The tls object
+   * will then call raw_send as a callback whenever data as been encrypted
+   * and can be sent on the socket.
+   */
+  void tls_send(std::string&& data);
+  /**
+   * Called by the tls object that some data has been decrypt. We call
+   * parse_in_buffer() to handle that unencrypted data.
+   */
+  void tls_record_received(uint64_t rec_no, const Botan::byte* data, size_t size) BOTAN_TLS_CALLBACKS_OVERRIDE;
+  /**
+   * Called by the tls object to indicate that some data has been encrypted
+   * and is now ready to be sent on the socket as is.
+   */
+  void tls_emit_data(const Botan::byte* data, size_t size) BOTAN_TLS_CALLBACKS_OVERRIDE;
+  /**
+   * Called by the tls object to indicate that a TLS alert has been
+   * received. We don’t use it, we just log some message, at the moment.
+   */
+  void tls_alert(Botan::TLS::Alert alert) BOTAN_TLS_CALLBACKS_OVERRIDE;
+  /**
+   * Called by the tls object at the end of the TLS handshake. We don't do
+   * anything here appart from logging the TLS session information.
+   */
+  bool tls_session_established(const Botan::TLS::Session& session) BOTAN_TLS_CALLBACKS_OVERRIDE;
+
+#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,34)
+  void tls_verify_cert_chain(const std::vector<Botan::X509_Certificate>& cert_chain,
+                             const std::vector<std::shared_ptr<const Botan::OCSP::Response>>& ocsp_responses,
+                             const std::vector<Botan::Certificate_Store*>& trusted_roots,
+                             Botan::Usage_Type usage,
+                             const std::string& hostname,
+                             const Botan::TLS::Policy& policy) BOTAN_TLS_CALLBACKS_OVERRIDE;
+#endif
+  /**
+   * Called whenever the tls session goes from inactive to active. This
+   * means that the handshake has just been successfully done, and we can
+   * now proceed to send any available data into our tls object.
+   */
+  void on_tls_activated();
+#endif // BOTAN_FOUND
+  /**
+   * Where data is added, when we want to send something to the client.
+   */
+  std::vector<std::string> out_buf;
+protected:
+  /**
+   * Whether we are using TLS on this connection or not.
+   */
+  bool use_tls;
+  /**
+   * Where data read from the socket is added until we can extract a full
+   * and meaningful “message” from it.
+   *
+   * TODO: something more efficient than a string.
+   */
+  std::string in_buf;
+  /**
+   * Remove the given “size” first bytes from our in_buf.
+   */
+  void consume_in_buffer(const std::size_t size);
+  /**
+   * Provide a buffer in which data can be directly received. This can be
+   * used to avoid copying data into in_buf before using it. If no buffer
+   * needs to be provided, nullptr is returned (the default implementation
+   * does that), in that case our internal in_buf will be used to save the
+   * data until it can be used by parse_in_buffer().
+   */
+  virtual void* get_receive_buffer(const size_t size) const;
+  /**
+   * Called when we detect a disconnection from the remote host.
+   */
+  virtual void on_connection_close(const std::string&) {}
+  virtual void on_connection_failed(const std::string&) {}
+
+#ifdef BOTAN_FOUND
+protected:
+  BasicCredentialsManager credential_manager;
+private:
+  /**
+   * We use a unique_ptr because we may not want to create the object at
+   * all. The Botan::TLS::Client object generates a handshake message and
+   * calls the output_fn callback with it as soon as it is created.
+   * Therefore, we do not want to create it if we do not intend to send any
+   * TLS-encrypted message. We create the object only when needed (for
+   * example after we have negociated a TLS session using a STARTTLS
+   * message, or stuf like that).
+   *
+   * See start_tls for the method where this object is created.
+   */
+  std::unique_ptr<Botan::TLS::Client> tls;
+  /**
+   * An additional buffer to keep data that the user wants to send, but
+   * cannot because the handshake is not done.
+   */
+  std::vector<Botan::byte> pre_buf;
+#endif // BOTAN_FOUND
+};
-- 
cgit v1.2.3


From 1a09c965eb3723cdaab9ea556f30ffbc7f09a6dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Wed, 29 Mar 2017 23:32:43 +0200
Subject: Remove two sneaky log_debug

---
 src/network/resolver.cpp | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

(limited to 'src/network')

diff --git a/src/network/resolver.cpp b/src/network/resolver.cpp
index db7fb32..ef54ba2 100644
--- a/src/network/resolver.cpp
+++ b/src/network/resolver.cpp
@@ -1,7 +1,7 @@
 #include <network/dns_handler.hpp>
 #include <utils/timed_events.hpp>
 #include <network/resolver.hpp>
-#include <string.h>
+#include <cstring>
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #ifdef UDNS_FOUND
@@ -41,8 +41,8 @@ Resolver::Resolver():
 void Resolver::resolve(const std::string& hostname, const std::string& port,
                        SuccessCallbackType success_cb, ErrorCallbackType error_cb)
 {
-  this->error_cb = error_cb;
-  this->success_cb = success_cb;
+  this->error_cb = std::move(error_cb);
+  this->success_cb = std::move(success_cb);
 #ifdef UDNS_FOUND
   this->port = port;
 #endif
@@ -52,8 +52,7 @@ void Resolver::resolve(const std::string& hostname, const std::string& port,
 
 int Resolver::call_getaddrinfo(const char *name, const char* port, int flags)
 {
-  struct addrinfo hints;
-  memset(&hints, 0, sizeof(struct addrinfo));
+  struct addrinfo hints{};
   hints.ai_flags = flags;
   hints.ai_family = AF_UNSPEC;
   hints.ai_socktype = SOCK_STREAM;
@@ -111,7 +110,7 @@ void Resolver::start_resolving(const std::string& hostname, const std::string& p
   // And finally, we try a DNS resolution
   auto hostname6_resolved = [](dns_ctx*, dns_rr_a6* result, void* data)
   {
-    Resolver* resolver = static_cast<Resolver*>(data);
+    auto resolver = static_cast<Resolver*>(data);
     resolver->on_hostname6_resolved(result);
     resolver->after_resolved();
     std::free(result);
@@ -119,7 +118,7 @@ void Resolver::start_resolving(const std::string& hostname, const std::string& p
 
   auto hostname4_resolved = [](dns_ctx*, dns_rr_a4* result, void* data)
   {
-    Resolver* resolver = static_cast<Resolver*>(data);
+    auto resolver = static_cast<Resolver*>(data);
     resolver->on_hostname4_resolved(result);
     resolver->after_resolved();
     std::free(result);
-- 
cgit v1.2.3


From 5402a256d1f0ebbeafa32d250d000cf38fe748fb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Fri, 7 Apr 2017 18:45:24 +0200
Subject: Apply all the clang-tidy modernize-* fixes

---
 src/network/poller.cpp                    | 4 ++--
 src/network/tcp_client_socket_handler.cpp | 2 +-
 src/network/tcp_server_socket.hpp         | 1 -
 src/network/tcp_socket_handler.cpp        | 2 +-
 4 files changed, 4 insertions(+), 5 deletions(-)

(limited to 'src/network')

diff --git a/src/network/poller.cpp b/src/network/poller.cpp
index 9f5bcfb..9f62e36 100644
--- a/src/network/poller.cpp
+++ b/src/network/poller.cpp
@@ -2,8 +2,8 @@
 #include <logger/logger.hpp>
 #include <utils/timed_events.hpp>
 
-#include <assert.h>
-#include <errno.h>
+#include <cassert>
+#include <cerrno>
 #include <stdio.h>
 #include <signal.h>
 #include <unistd.h>
diff --git a/src/network/tcp_client_socket_handler.cpp b/src/network/tcp_client_socket_handler.cpp
index 4628703..7181c9d 100644
--- a/src/network/tcp_client_socket_handler.cpp
+++ b/src/network/tcp_client_socket_handler.cpp
@@ -256,6 +256,6 @@ std::string TCPClientSocketHandler::get_port() const
 
 bool TCPClientSocketHandler::match_port_pairt(const uint16_t local, const uint16_t remote) const
 {
-  const uint16_t remote_port = static_cast<uint16_t>(std::stoi(this->port));
+  const auto remote_port = static_cast<uint16_t>(std::stoi(this->port));
   return this->is_connected() && local == this->local_port && remote == remote_port;
 }
diff --git a/src/network/tcp_server_socket.hpp b/src/network/tcp_server_socket.hpp
index c511962..652b773 100644
--- a/src/network/tcp_server_socket.hpp
+++ b/src/network/tcp_server_socket.hpp
@@ -12,7 +12,6 @@
 #include <netinet/ip.h>
 
 #include <cstring>
-#include <cassert>
 
 template <typename RemoteSocketType>
 class TcpSocketServer: public SocketHandler
diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp
index 7eebae0..b5e5db1 100644
--- a/src/network/tcp_socket_handler.cpp
+++ b/src/network/tcp_socket_handler.cpp
@@ -8,7 +8,7 @@
 #include <sys/types.h>
 #include <stdexcept>
 #include <unistd.h>
-#include <errno.h>
+#include <cerrno>
 #include <cstring>
 
 #ifdef BOTAN_FOUND
-- 
cgit v1.2.3


From ccb4ee098f0416ab47a46650705dba6495e8bec7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Fri, 7 Apr 2017 18:53:12 +0200
Subject: Apply all the clang-tidy misc-* fixes

---
 src/network/credentials_manager.cpp | 2 +-
 src/network/tcp_socket_handler.cpp  | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

(limited to 'src/network')

diff --git a/src/network/credentials_manager.cpp b/src/network/credentials_manager.cpp
index f9f8c94..0908a2f 100644
--- a/src/network/credentials_manager.cpp
+++ b/src/network/credentials_manager.cpp
@@ -98,7 +98,7 @@ bool BasicCredentialsManager::try_to_open_one_ca_bundle(const std::vector<std::s
               // because the certificate is signed by an issuer that was ignored.
               try {
                   Botan::X509_Certificate cert(bundle);
-                  BasicCredentialsManager::certificate_store.add_certificate(std::move(cert));
+                  BasicCredentialsManager::certificate_store.add_certificate(cert);
                 } catch (const Botan::Decoding_Error& error) {
                   continue;
                 }
diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp
index b5e5db1..1bb8bf3 100644
--- a/src/network/tcp_socket_handler.cpp
+++ b/src/network/tcp_socket_handler.cpp
@@ -42,7 +42,6 @@ namespace
 using namespace std::string_literals;
 using namespace std::chrono_literals;
 
-namespace ph = std::placeholders;
 
 TCPSocketHandler::TCPSocketHandler(std::shared_ptr<Poller>& poller):
   SocketHandler(poller, -1),
-- 
cgit v1.2.3


From be9c577de840c7f0dc08b9f5b9ba9bd522d0e2ea Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Fri, 7 Apr 2017 19:01:49 +0200
Subject: Apply all the clang-tidy performance-* fixes

---
 src/network/credentials_manager.cpp | 2 +-
 src/network/credentials_manager.hpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

(limited to 'src/network')

diff --git a/src/network/credentials_manager.cpp b/src/network/credentials_manager.cpp
index 0908a2f..ea76627 100644
--- a/src/network/credentials_manager.cpp
+++ b/src/network/credentials_manager.cpp
@@ -44,7 +44,7 @@ const std::string& BasicCredentialsManager::get_trusted_fingerprint() const
 
 void check_tls_certificate(const std::vector<Botan::X509_Certificate>& certs,
                            const std::string& hostname, const std::string& trusted_fingerprint,
-                           std::exception_ptr exc)
+                           const std::exception_ptr& exc)
 {
 
   if (!trusted_fingerprint.empty() && !certs.empty() &&
diff --git a/src/network/credentials_manager.hpp b/src/network/credentials_manager.hpp
index c463ad4..e7c247d 100644
--- a/src/network/credentials_manager.hpp
+++ b/src/network/credentials_manager.hpp
@@ -19,7 +19,7 @@ class TCPSocketHandler;
  */
 void check_tls_certificate(const std::vector<Botan::X509_Certificate>& certs,
                            const std::string& hostname, const std::string& trusted_fingerprint,
-                           std::exception_ptr exc);
+                           const std::exception_ptr& exc);
 
 class BasicCredentialsManager: public Botan::Credentials_Manager
 {
-- 
cgit v1.2.3


From 8a912ea3a3a74f6c4d2cb67adf3f60b4ec0a1c9f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Fri, 7 Apr 2017 11:10:51 +0200
Subject: Apply a few clang-tidy cppcoreguidelines-* fixes

---
 src/network/poller.cpp                    | 2 +-
 src/network/tcp_client_socket_handler.cpp | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

(limited to 'src/network')

diff --git a/src/network/poller.cpp b/src/network/poller.cpp
index 9f62e36..ca49180 100644
--- a/src/network/poller.cpp
+++ b/src/network/poller.cpp
@@ -198,7 +198,7 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
   static const size_t max_events = 12;
   struct epoll_event revents[max_events];
   // Unblock all signals, only during the epoll_pwait call
-  sigset_t empty_signal_set;
+  sigset_t empty_signal_set{};
   sigemptyset(&empty_signal_set);
   const int nb_events = ::epoll_pwait(this->epfd, revents, max_events, timeout.count(),
                                       &empty_signal_set);
diff --git a/src/network/tcp_client_socket_handler.cpp b/src/network/tcp_client_socket_handler.cpp
index 7181c9d..35f2446 100644
--- a/src/network/tcp_client_socket_handler.cpp
+++ b/src/network/tcp_client_socket_handler.cpp
@@ -35,7 +35,7 @@ void TCPClientSocketHandler::init_socket(const struct addrinfo* rp)
       // Convert the address from string format to a sockaddr that can be
       // used in bind()
       struct addrinfo* result;
-      struct addrinfo hints;
+      struct addrinfo hints{};
       memset(&hints, 0, sizeof(hints));
       hints.ai_flags = AI_NUMERICHOST;
       hints.ai_family = AF_UNSPEC;
@@ -161,14 +161,14 @@ void TCPClientSocketHandler::connect(const std::string& address, const std::stri
           this->local_port = static_cast<uint16_t>(-1);
           if (rp->ai_family == AF_INET6)
             {
-              struct sockaddr_in6 a;
+              struct sockaddr_in6 a{};
               socklen_t l = sizeof(a);
               if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1)
                 this->local_port = ntohs(a.sin6_port);
             }
           else if (rp->ai_family == AF_INET)
             {
-              struct sockaddr_in a;
+              struct sockaddr_in a{};
               socklen_t l = sizeof(a);
               if (::getsockname(this->socket, (struct sockaddr*)&a, &l) != -1)
                 this->local_port = ntohs(a.sin_port);
-- 
cgit v1.2.3


From 69fd6cc4c3b88443ce98ae32e34909654feb1e4f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Thu, 20 Apr 2017 19:14:59 +0200
Subject: Explicitely init the msghdr fields, to be compatible with any
 implementation

---
 src/network/tcp_socket_handler.cpp | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

(limited to 'src/network')

diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp
index 1bb8bf3..789a4a7 100644
--- a/src/network/tcp_socket_handler.cpp
+++ b/src/network/tcp_socket_handler.cpp
@@ -125,9 +125,14 @@ ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size)
 void TCPSocketHandler::on_send()
 {
   struct iovec msg_iov[UIO_FASTIOV] = {};
-  struct msghdr msg{nullptr, 0,
-      msg_iov,
-      0, nullptr, 0, 0};
+  struct msghdr msg;
+  msg.msg_name = nullptr;
+  msg.msg_namelen = 0;
+  msg.msg_iov = msg_iov;
+  msg.msg_iovlen = 0;
+  msg.msg_control = nullptr;
+  msg.msg_controllen = 0;
+  msg.msg_flags = 0;
   for (const std::string& s: this->out_buf)
     {
       // unconsting the content of s is ok, sendmsg will never modify it
-- 
cgit v1.2.3


From cf87cf089251eddf2c33322e07b0cde9f70ec24b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Thu, 20 Apr 2017 15:44:46 +0200
Subject: Better way to init the msghdr fields

---
 src/network/tcp_socket_handler.cpp | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

(limited to 'src/network')

diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp
index 789a4a7..02265ec 100644
--- a/src/network/tcp_socket_handler.cpp
+++ b/src/network/tcp_socket_handler.cpp
@@ -125,14 +125,9 @@ ssize_t TCPSocketHandler::do_recv(void* recv_buf, const size_t buf_size)
 void TCPSocketHandler::on_send()
 {
   struct iovec msg_iov[UIO_FASTIOV] = {};
-  struct msghdr msg;
-  msg.msg_name = nullptr;
-  msg.msg_namelen = 0;
+  struct msghdr msg{};
   msg.msg_iov = msg_iov;
   msg.msg_iovlen = 0;
-  msg.msg_control = nullptr;
-  msg.msg_controllen = 0;
-  msg.msg_flags = 0;
   for (const std::string& s: this->out_buf)
     {
       // unconsting the content of s is ok, sendmsg will never modify it
-- 
cgit v1.2.3


From 7b3e0e0cf3eddd3537455a3605b04a48ee663f47 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Sun, 30 Apr 2017 15:04:40 +0200
Subject: =?UTF-8?q?Make=20botan=E2=80=99s=20policy=20configurable=20from?=
 =?UTF-8?q?=20a=20file?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

fix #3244
---
 src/network/tcp_socket_handler.cpp | 14 ++++++-----
 src/network/tcp_socket_handler.hpp | 17 ++------------
 src/network/tls_policy.cpp         | 48 ++++++++++++++++++++++++++++++++++++++
 src/network/tls_policy.hpp         | 28 ++++++++++++++++++++++
 4 files changed, 86 insertions(+), 21 deletions(-)
 create mode 100644 src/network/tls_policy.cpp
 create mode 100644 src/network/tls_policy.hpp

(limited to 'src/network')

diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp
index 02265ec..1bd5315 100644
--- a/src/network/tcp_socket_handler.cpp
+++ b/src/network/tcp_socket_handler.cpp
@@ -14,6 +14,8 @@
 #ifdef BOTAN_FOUND
 # include <botan/hex.h>
 # include <botan/tls_exceptn.h>
+# include <config/config.hpp>
+# include <utils/dirname.hpp>
 
 namespace
 {
@@ -22,11 +24,6 @@ namespace
       static Botan::AutoSeeded_RNG rng{};
       return rng;
     }
-    BiboumiTLSPolicy& get_policy()
-    {
-      static BiboumiTLSPolicy policy{};
-      return policy;
-    }
     Botan::TLS::Session_Manager_In_Memory& get_session_manager()
     {
       static Botan::TLS::Session_Manager_In_Memory session_manager{get_rng()};
@@ -233,6 +230,11 @@ void TCPSocketHandler::consume_in_buffer(const std::size_t size)
 void TCPSocketHandler::start_tls(const std::string& address, const std::string& port)
 {
   Botan::TLS::Server_Information server_info(address, "irc", std::stoul(port));
+  auto policy_directory = Config::get("policy_directory", utils::dirname(Config::get_filename()));
+  if (!policy_directory.empty() && policy_directory[policy_directory.size()-1] != '/')
+    policy_directory += '/';
+  this->policy.load(policy_directory + "policy.txt");
+  this->policy.load(policy_directory + address + ".policy.txt");
   this->tls = std::make_unique<Botan::TLS::Client>(
 # if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32)
       *this,
@@ -242,7 +244,7 @@ void TCPSocketHandler::start_tls(const std::string& address, const std::string&
       [this](Botan::TLS::Alert alert, const Botan::byte*, size_t) { this->tls_alert(alert); },
       [this](const Botan::TLS::Session& session) { return this->tls_session_established(session); },
 # endif
-      get_session_manager(), this->credential_manager, get_policy(),
+      get_session_manager(), this->credential_manager, this->policy,
       get_rng(), server_info, Botan::TLS::Protocol_Version::latest_tls_version());
 }
 
diff --git a/src/network/tcp_socket_handler.hpp b/src/network/tcp_socket_handler.hpp
index ba23861..f68698e 100644
--- a/src/network/tcp_socket_handler.hpp
+++ b/src/network/tcp_socket_handler.hpp
@@ -23,21 +23,7 @@
 # include <botan/types.h>
 # include <botan/botan.h>
 # include <botan/tls_session_manager.h>
-
-class BiboumiTLSPolicy: public Botan::TLS::Policy
-{
-public:
-# if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,33)
-  bool use_ecc_point_compression() const override
-  {
-    return true;
-  }
-  bool require_cert_revocation_info() const override
-  {
-    return false;
-  }
-# endif
-};
+# include <network/tls_policy.hpp>
 
 # if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,11,32)
 #  define BOTAN_TLS_CALLBACKS_OVERRIDE override final
@@ -230,6 +216,7 @@ protected:
 protected:
   BasicCredentialsManager credential_manager;
 private:
+  BiboumiTLSPolicy policy;
   /**
    * We use a unique_ptr because we may not want to create the object at
    * all. The Botan::TLS::Client object generates a handshake message and
diff --git a/src/network/tls_policy.cpp b/src/network/tls_policy.cpp
new file mode 100644
index 0000000..5439397
--- /dev/null
+++ b/src/network/tls_policy.cpp
@@ -0,0 +1,48 @@
+#include "biboumi.h"
+
+#ifdef BOTAN_FOUND
+
+#include <fstream>
+
+#include <utils/tolower.hpp>
+
+#include <network/tls_policy.hpp>
+#include <logger/logger.hpp>
+
+bool BiboumiTLSPolicy::load(const std::string& filename)
+{
+  std::ifstream is(filename.data());
+  if (is)
+    {
+      try {
+          this->load(is);
+          log_info("Successfully loaded policy file: ", filename);
+          return true;
+        } catch (const Botan::Exception& e) {
+          log_error("Failed to parse policy_file ", filename, ": ", e.what());
+          return false;
+        }
+    }
+  log_info("Could not open policy file: ", filename);
+  return false;
+}
+
+void BiboumiTLSPolicy::load(std::istream& is)
+{
+  const auto dict = Botan::read_cfg(is);
+  for (const auto& pair: dict)
+    {
+      // Workaround for options that are not overridden in Botan::TLS::Text_Policy
+      if (pair.first == "require_cert_revocation_info")
+        this->req_cert_revocation_info = !(pair.second == "0" || utils::tolower(pair.second) == "false");
+      else
+        this->set(pair.first, pair.second);
+    }
+}
+
+bool BiboumiTLSPolicy::require_cert_revocation_info() const
+{
+  return this->req_cert_revocation_info;
+}
+
+#endif
diff --git a/src/network/tls_policy.hpp b/src/network/tls_policy.hpp
new file mode 100644
index 0000000..29fd2b3
--- /dev/null
+++ b/src/network/tls_policy.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "biboumi.h"
+
+#ifdef BOTAN_FOUND
+
+#include <botan/tls_policy.h>
+
+class BiboumiTLSPolicy: public Botan::TLS::Text_Policy
+{
+public:
+  BiboumiTLSPolicy():
+      Botan::TLS::Text_Policy({})
+  {}
+  bool load(const std::string& filename);
+  void load(std::istream& iss);
+
+  BiboumiTLSPolicy(const BiboumiTLSPolicy &) = delete;
+  BiboumiTLSPolicy(BiboumiTLSPolicy &&) = delete;
+  BiboumiTLSPolicy &operator=(const BiboumiTLSPolicy &) = delete;
+  BiboumiTLSPolicy &operator=(BiboumiTLSPolicy &&) = delete;
+
+  bool require_cert_revocation_info() const override;
+protected:
+  bool req_cert_revocation_info{true};
+};
+
+#endif
-- 
cgit v1.2.3


From f7e4adb10bff1c278a8543b230b10881ff3799fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Tue, 9 May 2017 15:46:20 +0200
Subject: Avoid any potential int overflow

---
 src/network/poller.cpp             | 6 +++++-
 src/network/tcp_socket_handler.cpp | 5 +++--
 2 files changed, 8 insertions(+), 3 deletions(-)

(limited to 'src/network')

diff --git a/src/network/poller.cpp b/src/network/poller.cpp
index ca49180..0f02cc5 100644
--- a/src/network/poller.cpp
+++ b/src/network/poller.cpp
@@ -200,7 +200,11 @@ int Poller::poll(const std::chrono::milliseconds& timeout)
   // Unblock all signals, only during the epoll_pwait call
   sigset_t empty_signal_set{};
   sigemptyset(&empty_signal_set);
-  const int nb_events = ::epoll_pwait(this->epfd, revents, max_events, timeout.count(),
+
+  int real_timeout = std::numeric_limits<int>::max();
+  if (timeout.count() < real_timeout) // Just avoid any potential int overflow
+    real_timeout = static_cast<int>(timeout.count());
+  const int nb_events = ::epoll_pwait(this->epfd, revents, max_events, real_timeout,
                                       &empty_signal_set);
   if (nb_events == -1)
     {
diff --git a/src/network/tcp_socket_handler.cpp b/src/network/tcp_socket_handler.cpp
index 1bd5315..1049375 100644
--- a/src/network/tcp_socket_handler.cpp
+++ b/src/network/tcp_socket_handler.cpp
@@ -227,9 +227,10 @@ void TCPSocketHandler::consume_in_buffer(const std::size_t size)
 }
 
 #ifdef BOTAN_FOUND
-void TCPSocketHandler::start_tls(const std::string& address, const std::string& port)
+void TCPSocketHandler::start_tls(const std::string& address, const std::string& port_string)
 {
-  Botan::TLS::Server_Information server_info(address, "irc", std::stoul(port));
+  auto port = std::min(std::stoul(port_string), static_cast<unsigned long>(std::numeric_limits<uint16_t>::max()));
+  Botan::TLS::Server_Information server_info(address, "irc", static_cast<uint16_t>(port));
   auto policy_directory = Config::get("policy_directory", utils::dirname(Config::get_filename()));
   if (!policy_directory.empty() && policy_directory[policy_directory.size()-1] != '/')
     policy_directory += '/';
-- 
cgit v1.2.3


From da55060840631bb63978a67270066b009b9c9270 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?louiz=E2=80=99?= <louiz@louiz.org>
Date: Wed, 10 May 2017 16:48:51 +0200
Subject: Improve the handling of the biboudb.hpp dependencies

---
 src/network/credentials_manager.cpp | 4 ----
 1 file changed, 4 deletions(-)

(limited to 'src/network')

diff --git a/src/network/credentials_manager.cpp b/src/network/credentials_manager.cpp
index ea76627..f93a366 100644
--- a/src/network/credentials_manager.cpp
+++ b/src/network/credentials_manager.cpp
@@ -7,10 +7,6 @@
 #include <botan/tls_exceptn.h>
 #include <config/config.hpp>
 
-#ifdef USE_DATABASE
-# include <database/database.hpp>
-#endif
-
 /**
  * TODO find a standard way to find that out.
  */
-- 
cgit v1.2.3