diff options
author | Florent Le Coz <louiz@louiz.org> | 2014-07-09 13:02:37 +0200 |
---|---|---|
committer | Florent Le Coz <louiz@louiz.org> | 2015-02-23 13:56:08 +0100 |
commit | b86547dc1ef407ca3838444533bc7145e32a0d90 (patch) | |
tree | 3aa692def62796a5debe705ebc29d40b311e38cb /src/network/dns_handler.cpp | |
parent | a17135720e77c03e66679852198e46a070d56f4d (diff) | |
download | biboumi-b86547dc1ef407ca3838444533bc7145e32a0d90.tar.gz biboumi-b86547dc1ef407ca3838444533bc7145e32a0d90.tar.bz2 biboumi-b86547dc1ef407ca3838444533bc7145e32a0d90.tar.xz biboumi-b86547dc1ef407ca3838444533bc7145e32a0d90.zip |
Implement async DNS resolution using c-ares
fix #2533
Diffstat (limited to 'src/network/dns_handler.cpp')
-rw-r--r-- | src/network/dns_handler.cpp | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/src/network/dns_handler.cpp b/src/network/dns_handler.cpp new file mode 100644 index 0000000..45bf626 --- /dev/null +++ b/src/network/dns_handler.cpp @@ -0,0 +1,112 @@ +#include <config.h> +#ifdef CARES_FOUND + +#include <network/dns_socket_handler.hpp> +#include <network/tcp_socket_handler.hpp> +#include <network/dns_handler.hpp> +#include <network/poller.hpp> + +#include <algorithm> +#include <stdexcept> + +DNSHandler DNSHandler::instance; + +using namespace std::string_literals; + +void on_hostname4_resolved(void* arg, int status, int, struct hostent* hostent) +{ + TCPSocketHandler* socket_handler = static_cast<TCPSocketHandler*>(arg); + socket_handler->on_hostname4_resolved(status, hostent); +} + +void on_hostname6_resolved(void* arg, int status, int, struct hostent* hostent) +{ + TCPSocketHandler* socket_handler = static_cast<TCPSocketHandler*>(arg); + socket_handler->on_hostname6_resolved(status, hostent); +} + +DNSHandler::DNSHandler() +{ + 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)); + if ((ares_error = ::ares_init(&this->channel)) != 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->socket_handlers.clear(); + ::ares_destroy(this->channel); + ::ares_library_cleanup(); +} + +void DNSHandler::gethostbyname(const std::string& name, + TCPSocketHandler* socket_handler, int family) +{ + socket_handler->free_cares_addrinfo(); + if (family == AF_INET) + ::ares_gethostbyname(this->channel, name.data(), family, + &::on_hostname4_resolved, socket_handler); + else + ::ares_gethostbyname(this->channel, name.data(), family, + &::on_hostname6_resolved, socket_handler); +} + +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_front(std::make_unique<DNSSocketHandler>(poller, i)); + it = this->socket_handlers.begin(); + } + poller->add_socket_handler(it->get()); + if (write) + poller->watch_send_events(it->get()); + } + } +} + +#endif /* CARES_FOUND */ |