From b86547dc1ef407ca3838444533bc7145e32a0d90 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Wed, 9 Jul 2014 13:02:37 +0200 Subject: Implement async DNS resolution using c-ares fix #2533 --- src/network/dns_handler.cpp | 112 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 src/network/dns_handler.cpp (limited to 'src/network/dns_handler.cpp') 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 +#ifdef CARES_FOUND + +#include +#include +#include +#include + +#include +#include + +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(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(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) +{ + 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(poller, i)); + it = this->socket_handlers.begin(); + } + poller->add_socket_handler(it->get()); + if (write) + poller->watch_send_events(it->get()); + } + } +} + +#endif /* CARES_FOUND */ -- cgit v1.2.3