summaryrefslogtreecommitdiff
path: root/louloulibs/network/dns_handler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'louloulibs/network/dns_handler.cpp')
-rw-r--r--louloulibs/network/dns_handler.cpp134
1 files changed, 134 insertions, 0 deletions
diff --git a/louloulibs/network/dns_handler.cpp b/louloulibs/network/dns_handler.cpp
new file mode 100644
index 0000000..e267944
--- /dev/null
+++ b/louloulibs/network/dns_handler.cpp
@@ -0,0 +1,134 @@
+#include <louloulibs.h>
+#ifdef CARES_FOUND
+
+#include <network/dns_socket_handler.hpp>
+#include <network/dns_handler.hpp>
+#include <network/poller.hpp>
+
+#include <utils/timed_events.hpp>
+
+#include <algorithm>
+#include <stdexcept>
+
+DNSHandler DNSHandler::instance;
+
+using namespace std::string_literals;
+DNSHandler::DNSHandler():
+ socket_handlers{},
+ channel{nullptr}
+{
+ int ares_error;
+ if ((ares_error = ::ares_library_init(ARES_LIB_INIT_ALL)) != 0)
+ throw std::runtime_error("Failed to initialize c-ares lib: "s + ares_strerror(ares_error));
+ struct ares_options options = {};
+ // The default timeout values are way too high
+ options.timeout = 1000;
+ options.tries = 3;
+ if ((ares_error = ::ares_init_options(&this->channel,
+ &options,
+ ARES_OPT_TIMEOUTMS|ARES_OPT_TRIES)) != ARES_SUCCESS)
+ throw std::runtime_error("Failed to initialize c-ares channel: "s + ares_strerror(ares_error));
+}
+
+ares_channel& DNSHandler::get_channel()
+{
+ return this->channel;
+}
+
+void DNSHandler::destroy()
+{
+ this->remove_all_sockets_from_poller();
+ this->socket_handlers.clear();
+ ::ares_destroy(this->channel);
+ ::ares_library_cleanup();
+}
+
+void DNSHandler::gethostbyname(const std::string& name, ares_host_callback callback,
+ void* data, int family)
+{
+ if (family == AF_INET)
+ ::ares_gethostbyname(this->channel, name.data(), family,
+ callback, data);
+ else
+ ::ares_gethostbyname(this->channel, name.data(), family,
+ callback, data);
+}
+
+void DNSHandler::watch_dns_sockets(std::shared_ptr<Poller>& poller)
+{
+ fd_set readers;
+ fd_set writers;
+
+ FD_ZERO(&readers);
+ FD_ZERO(&writers);
+
+ int ndfs = ::ares_fds(this->channel, &readers, &writers);
+ // For each existing DNS socket, see if we are still supposed to watch it,
+ // if not then erase it
+ this->socket_handlers.erase(
+ std::remove_if(this->socket_handlers.begin(), this->socket_handlers.end(),
+ [&readers](const auto& dns_socket)
+ {
+ return !FD_ISSET(dns_socket->get_socket(), &readers);
+ }),
+ this->socket_handlers.end());
+
+ for (auto i = 0; i < ndfs; ++i)
+ {
+ bool read = FD_ISSET(i, &readers);
+ bool write = FD_ISSET(i, &writers);
+ // Look for the DNSSocketHandler with this fd
+ auto it = std::find_if(this->socket_handlers.begin(),
+ this->socket_handlers.end(),
+ [i](const auto& socket_handler)
+ {
+ return i == socket_handler->get_socket();
+ });
+ if (!read && !write) // No need to read or write to it
+ { // If found, erase it and stop watching it because it is not
+ // needed anymore
+ if (it != this->socket_handlers.end())
+ // The socket destructor removes it from the poller
+ this->socket_handlers.erase(it);
+ }
+ else // We need to write and/or read to it
+ { // If not found, create it because we need to watch it
+ if (it == this->socket_handlers.end())
+ {
+ this->socket_handlers.emplace(this->socket_handlers.begin(),
+ std::make_unique<DNSSocketHandler>(poller, *this, i));
+ it = this->socket_handlers.begin();
+ }
+ poller->add_socket_handler(it->get());
+ if (write)
+ poller->watch_send_events(it->get());
+ }
+ }
+ // Cancel previous timer, if any.
+ TimedEventsManager::instance().cancel("DNS timeout");
+ struct timeval tv;
+ struct timeval* tvp;
+ tvp = ::ares_timeout(this->channel, NULL, &tv);
+ if (tvp)
+ {
+ auto future_time = std::chrono::steady_clock::now() + std::chrono::seconds(tvp->tv_sec) + \
+ std::chrono::microseconds(tvp->tv_usec);
+ TimedEventsManager::instance().add_event(TimedEvent(std::move(future_time),
+ [this]()
+ {
+ for (auto& dns_socket_handler: this->socket_handlers)
+ dns_socket_handler->on_recv();
+ },
+ "DNS timeout"));
+ }
+}
+
+void DNSHandler::remove_all_sockets_from_poller()
+{
+ for (const auto& socket_handler: this->socket_handlers)
+ {
+ socket_handler->remove_from_poller();
+ }
+}
+
+#endif /* CARES_FOUND */