diff options
Diffstat (limited to 'louloulibs/network/resolver.cpp')
-rw-r--r-- | louloulibs/network/resolver.cpp | 280 |
1 files changed, 169 insertions, 111 deletions
diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp index 3847d87..efb0cf0 100644 --- a/louloulibs/network/resolver.cpp +++ b/louloulibs/network/resolver.cpp @@ -1,19 +1,32 @@ #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> +#include <udns.h> +#include <fstream> #include <cstdlib> +#include <sstream> +#include <chrono> +#include <map> using namespace std::string_literals; +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"} +}; + Resolver::Resolver(): -#ifdef CARES_FOUND +#ifdef UDNS_FOUND resolved4(false), resolved6(false), resolving(false), - cares_addrinfo(nullptr), port{}, #endif resolved(false), @@ -26,15 +39,44 @@ void Resolver::resolve(const std::string& hostname, const std::string& port, { this->error_cb = error_cb; this->success_cb = success_cb; -#ifdef CARES_FOUND +#ifdef UDNS_FOUND this->port = port; #endif this->start_resolving(hostname, port); } -#ifdef CARES_FOUND -void Resolver::start_resolving(const std::string& hostname, const std::string&) +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; @@ -42,48 +84,139 @@ void Resolver::start_resolving(const std::string& hostname, const std::string&) this->resolved6 = false; this->error_msg.clear(); - this->cares_addrinfo = nullptr; + this->addr.reset(nullptr); - auto hostname4_resolved = [](void* arg, int status, int, - struct hostent* hostent) + // 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) { - Resolver* resolver = static_cast<Resolver*>(arg); - resolver->on_hostname4_resolved(status, hostent); - }; - auto hostname6_resolved = [](void* arg, int status, int, - struct hostent* hostent) + 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); + }; + + auto hostname4_resolved = [](dns_ctx*, dns_rr_a4* result, void* data) + { + Resolver* resolver = static_cast<Resolver*>(data); + resolver->on_hostname4_resolved(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)) { - Resolver* resolver = static_cast<Resolver*>(arg); - resolver->on_hostname6_resolved(status, hostent); - }; - - DNSHandler::instance.gethostbyname(hostname, hostname6_resolved, - this, AF_INET6); - DNSHandler::instance.gethostbyname(hostname, hostname4_resolved, - this, AF_INET); + 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(int status, struct hostent* hostent) +void Resolver::on_hostname4_resolved(dns_rr_a4 *result) { + if (dns_active(nullptr) == 0) + DNSHandler::unwatch(); + this->resolved4 = true; - if (status == ARES_SUCCESS) - this->fill_ares_addrinfo4(hostent); + + 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 - this->error_msg = ::ares_strerror(status); + { + const auto error = dns_error_messages.find(status); + if (error != end(dns_error_messages)) + this->error_msg = error->second; + } - if (this->resolved4 && this->resolved6) + if (this->resolved6 && this->resolved4) this->on_resolved(); } -void Resolver::on_hostname6_resolved(int status, struct hostent* hostent) +void Resolver::on_hostname6_resolved(dns_rr_a6 *result) { + if (dns_active(nullptr) == 0) + DNSHandler::unwatch(); + this->resolved6 = true; - if (status == ARES_SUCCESS) - this->fill_ares_addrinfo6(hostent); - else - this->error_msg = ::ares_strerror(status); + char buf[INET6_ADDRSTRLEN]; + + const auto status = dns_status(nullptr); - if (this->resolved4 && this->resolved6) + if (status >= 0 && result) + { + 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); + } + } + + if (this->resolved6 && this->resolved4) this->on_resolved(); } @@ -91,100 +224,26 @@ void Resolver::on_resolved() { this->resolved = true; this->resolving = false; - if (!this->cares_addrinfo) + if (!this->addr) { if (this->error_cb) this->error_cb(this->error_msg.data()); } else { - this->addr.reset(this->cares_addrinfo); if (this->success_cb) this->success_cb(this->addr.get()); } } -void Resolver::fill_ares_addrinfo4(const struct hostent* hostent) -{ - struct addrinfo* prev = this->cares_addrinfo; - struct in_addr** address = reinterpret_cast<struct in_addr**>(hostent->h_addr_list); - - while (*address) - { - // Create a new addrinfo list element, and fill it - struct addrinfo* current = new struct addrinfo; - current->ai_flags = 0; - current->ai_family = hostent->h_addrtype; - current->ai_socktype = SOCK_STREAM; - current->ai_protocol = 0; - current->ai_addrlen = sizeof(struct sockaddr_in); - - struct sockaddr_in* ai_addr = new struct sockaddr_in; - - ai_addr->sin_family = hostent->h_addrtype; - ai_addr->sin_port = htons(std::strtoul(this->port.data(), nullptr, 10)); - ai_addr->sin_addr.s_addr = (*address)->s_addr; - - current->ai_addr = reinterpret_cast<struct sockaddr*>(ai_addr); - current->ai_next = nullptr; - current->ai_canonname = nullptr; - - current->ai_next = prev; - this->cares_addrinfo = current; - prev = current; - ++address; - } -} - -void Resolver::fill_ares_addrinfo6(const struct hostent* hostent) -{ - struct addrinfo* prev = this->cares_addrinfo; - struct in6_addr** address = reinterpret_cast<struct in6_addr**>(hostent->h_addr_list); - - while (*address) - { - // Create a new addrinfo list element, and fill it - struct addrinfo* current = new struct addrinfo; - current->ai_flags = 0; - current->ai_family = hostent->h_addrtype; - current->ai_socktype = SOCK_STREAM; - current->ai_protocol = 0; - current->ai_addrlen = sizeof(struct sockaddr_in6); - - struct sockaddr_in6* ai_addr = new struct sockaddr_in6; - ai_addr->sin6_family = hostent->h_addrtype; - ai_addr->sin6_port = htons(std::strtoul(this->port.data(), nullptr, 10)); - ::memcpy(ai_addr->sin6_addr.s6_addr, (*address)->s6_addr, sizeof(ai_addr->sin6_addr.s6_addr)); - ai_addr->sin6_flowinfo = 0; - ai_addr->sin6_scope_id = 0; - - current->ai_addr = reinterpret_cast<struct sockaddr*>(ai_addr); - current->ai_canonname = nullptr; - - current->ai_next = prev; - this->cares_addrinfo = current; - prev = current; - ++address; - } -} - -#else // ifdef CARES_FOUND +#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); - struct addrinfo hints; - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_flags = 0; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = 0; - - struct addrinfo* addr_res = nullptr; - const int res = ::getaddrinfo(hostname.data(), port.data(), - &hints, &addr_res); + const auto res = this->call_getaddrinfo(hostname.data(), port.data(), 0); this->resolved = true; @@ -196,12 +255,11 @@ void Resolver::start_resolving(const std::string& hostname, const std::string& p } else { - this->addr.reset(addr_res); if (this->success_cb) this->success_cb(this->addr.get()); } } -#endif // ifdef CARES_FOUND +#endif // ifdef UDNS_FOUND std::string addr_to_string(const struct addrinfo* rp) { |