summaryrefslogtreecommitdiff
path: root/louloulibs/network
diff options
context:
space:
mode:
Diffstat (limited to 'louloulibs/network')
-rw-r--r--louloulibs/network/credentials_manager.cpp140
-rw-r--r--louloulibs/network/credentials_manager.hpp55
-rw-r--r--louloulibs/network/dns_handler.cpp46
-rw-r--r--louloulibs/network/dns_handler.hpp37
-rw-r--r--louloulibs/network/dns_socket_handler.cpp43
-rw-r--r--louloulibs/network/dns_socket_handler.hpp33
-rw-r--r--louloulibs/network/poller.cpp234
-rw-r--r--louloulibs/network/poller.hpp98
-rw-r--r--louloulibs/network/resolver.cpp281
-rw-r--r--louloulibs/network/resolver.hpp122
-rw-r--r--louloulibs/network/socket_handler.hpp42
-rw-r--r--louloulibs/network/tcp_client_socket_handler.cpp261
-rw-r--r--louloulibs/network/tcp_client_socket_handler.hpp82
-rw-r--r--louloulibs/network/tcp_server_socket.hpp70
-rw-r--r--louloulibs/network/tcp_socket_handler.cpp358
-rw-r--r--louloulibs/network/tcp_socket_handler.hpp251
16 files changed, 0 insertions, 2153 deletions
diff --git a/louloulibs/network/credentials_manager.cpp b/louloulibs/network/credentials_manager.cpp
deleted file mode 100644
index 289307b..0000000
--- a/louloulibs/network/credentials_manager.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-#include "louloulibs.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/louloulibs/network/credentials_manager.hpp b/louloulibs/network/credentials_manager.hpp
deleted file mode 100644
index 29ee024..0000000
--- a/louloulibs/network/credentials_manager.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma once
-
-#include "louloulibs.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/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp
deleted file mode 100644
index 641c087..0000000
--- a/louloulibs/network/dns_handler.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-#include <louloulibs.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/louloulibs/network/dns_handler.hpp b/louloulibs/network/dns_handler.hpp
deleted file mode 100644
index 416f85f..0000000
--- a/louloulibs/network/dns_handler.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-
-#include <louloulibs.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/louloulibs/network/dns_socket_handler.cpp b/louloulibs/network/dns_socket_handler.cpp
deleted file mode 100644
index 84e5625..0000000
--- a/louloulibs/network/dns_socket_handler.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#include <louloulibs.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/louloulibs/network/dns_socket_handler.hpp b/louloulibs/network/dns_socket_handler.hpp
deleted file mode 100644
index fc5f41f..0000000
--- a/louloulibs/network/dns_socket_handler.hpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include <louloulibs.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/louloulibs/network/poller.cpp b/louloulibs/network/poller.cpp
deleted file mode 100644
index 9f5bcfb..0000000
--- a/louloulibs/network/poller.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-#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/louloulibs/network/poller.hpp b/louloulibs/network/poller.hpp
deleted file mode 100644
index e39e438..0000000
--- a/louloulibs/network/poller.hpp
+++ /dev/null
@@ -1,98 +0,0 @@
-#pragma once
-
-
-#include <network/socket_handler.hpp>
-
-#include <unordered_map>
-#include <memory>
-#include <chrono>
-
-#define POLL 1
-#define EPOLL 2
-#define KQUEUE 3
-#include <louloulibs.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/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp
deleted file mode 100644
index db7fb32..0000000
--- a/louloulibs/network/resolver.cpp
+++ /dev/null
@@ -1,281 +0,0 @@
-#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/louloulibs/network/resolver.hpp b/louloulibs/network/resolver.hpp
deleted file mode 100644
index a560819..0000000
--- a/louloulibs/network/resolver.hpp
+++ /dev/null
@@ -1,122 +0,0 @@
-#pragma once
-
-#include "louloulibs.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/louloulibs/network/socket_handler.hpp b/louloulibs/network/socket_handler.hpp
deleted file mode 100644
index 6a7220e..0000000
--- a/louloulibs/network/socket_handler.hpp
+++ /dev/null
@@ -1,42 +0,0 @@
-#pragma once
-
-#include <louloulibs.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/louloulibs/network/tcp_client_socket_handler.cpp b/louloulibs/network/tcp_client_socket_handler.cpp
deleted file mode 100644
index 4628703..0000000
--- a/louloulibs/network/tcp_client_socket_handler.cpp
+++ /dev/null
@@ -1,261 +0,0 @@
-#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/louloulibs/network/tcp_client_socket_handler.hpp b/louloulibs/network/tcp_client_socket_handler.hpp
deleted file mode 100644
index 74caca9..0000000
--- a/louloulibs/network/tcp_client_socket_handler.hpp
+++ /dev/null
@@ -1,82 +0,0 @@
-#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/louloulibs/network/tcp_server_socket.hpp b/louloulibs/network/tcp_server_socket.hpp
deleted file mode 100644
index c511962..0000000
--- a/louloulibs/network/tcp_server_socket.hpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#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/louloulibs/network/tcp_socket_handler.cpp b/louloulibs/network/tcp_socket_handler.cpp
deleted file mode 100644
index 7eebae0..0000000
--- a/louloulibs/network/tcp_socket_handler.cpp
+++ /dev/null
@@ -1,358 +0,0 @@
-#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/louloulibs/network/tcp_socket_handler.hpp b/louloulibs/network/tcp_socket_handler.hpp
deleted file mode 100644
index 3ee2f47..0000000
--- a/louloulibs/network/tcp_socket_handler.hpp
+++ /dev/null
@@ -1,251 +0,0 @@
-#pragma once
-
-#include "louloulibs.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
-};