#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 {};
}