diff options
Diffstat (limited to 'louloulibs/network/resolver.cpp')
-rw-r--r-- | louloulibs/network/resolver.cpp | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/louloulibs/network/resolver.cpp b/louloulibs/network/resolver.cpp new file mode 100644 index 0000000..9d6de23 --- /dev/null +++ b/louloulibs/network/resolver.cpp @@ -0,0 +1,214 @@ +#include <network/dns_handler.hpp> +#include <network/resolver.hpp> +#include <string.h> +#include <arpa/inet.h> + +using namespace std::string_literals; + +Resolver::Resolver(): +#ifdef CARES_FOUND + resolved4(false), + resolved6(false), + resolving(false), + cares_addrinfo(nullptr), + 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 CARES_FOUND + this->port = port; +#endif + + this->start_resolving(hostname, port); +} + +#ifdef CARES_FOUND +void Resolver::start_resolving(const std::string& hostname, const std::string&) +{ + this->resolving = true; + this->resolved = false; + this->resolved4 = false; + this->resolved6 = false; + + this->error_msg.clear(); + this->cares_addrinfo = nullptr; + + auto hostname4_resolved = [](void* arg, int status, int, + struct hostent* hostent) + { + Resolver* resolver = static_cast<Resolver*>(arg); + resolver->on_hostname4_resolved(status, hostent); + }; + auto hostname6_resolved = [](void* arg, int status, int, + struct hostent* hostent) + { + 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); +} + +void Resolver::on_hostname4_resolved(int status, struct hostent* hostent) +{ + this->resolved4 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo4(hostent); + else + this->error_msg = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + this->on_resolved(); +} + +void Resolver::on_hostname6_resolved(int status, struct hostent* hostent) +{ + this->resolved6 = true; + if (status == ARES_SUCCESS) + this->fill_ares_addrinfo6(hostent); + else + this->error_msg = ::ares_strerror(status); + + if (this->resolved4 && this->resolved6) + this->on_resolved(); +} + +void Resolver::on_resolved() +{ + this->resolved = true; + this->resolving = false; + if (!this->cares_addrinfo) + { + 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* addr = new struct sockaddr_in; + addr->sin_family = hostent->h_addrtype; + addr->sin_port = htons(strtoul(this->port.data(), nullptr, 10)); + addr->sin_addr.s_addr = (*address)->s_addr; + + current->ai_addr = reinterpret_cast<struct sockaddr*>(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* addr = new struct sockaddr_in6; + addr->sin6_family = hostent->h_addrtype; + addr->sin6_port = htons(strtoul(this->port.data(), nullptr, 10)); + ::memcpy(addr->sin6_addr.s6_addr, (*address)->s6_addr, 16); + addr->sin6_flowinfo = 0; + addr->sin6_scope_id = 0; + + current->ai_addr = reinterpret_cast<struct sockaddr*>(addr); + current->ai_canonname = nullptr; + + current->ai_next = prev; + this->cares_addrinfo = current; + prev = current; + ++address; + } +} + +#else // ifdef CARES_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); + + this->resolved = true; + + if (res != 0) + { + this->error_msg = gai_strerror(res); + if (this->error_cb) + this->error_cb(this->error_msg.data()); + } + else + { + this->addr.reset(addr_res); + if (this->success_cb) + this->success_cb(this->addr.get()); + } +} +#endif // ifdef CARES_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 {}; +} |