1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
#include <louloulibs.h>
#ifdef CARES_FOUND
#include <network/dns_socket_handler.hpp>
#include <network/dns_handler.hpp>
#include <network/poller.hpp>
#include <utils/timed_events.hpp>
#include <algorithm>
#include <stdexcept>
DNSHandler DNSHandler::instance;
using namespace std::string_literals;
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));
struct ares_options options = {};
// The default timeout values are way too high
options.timeout = 1000;
options.tries = 3;
if ((ares_error = ::ares_init_options(&this->channel,
&options,
ARES_OPT_TIMEOUTMS|ARES_OPT_TRIES)) != 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, ares_host_callback callback,
void* data, int family)
{
if (family == AF_INET)
::ares_gethostbyname(this->channel, name.data(), family,
callback, data);
else
::ares_gethostbyname(this->channel, name.data(), family,
callback, data);
}
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(this->socket_handlers.begin(),
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());
}
}
// Cancel previous timer, if any.
TimedEventsManager::instance().cancel("DNS timeout");
struct timeval tv;
struct timeval* tvp;
tvp = ::ares_timeout(this->channel, NULL, &tv);
if (tvp)
{
auto future_time = std::chrono::steady_clock::now() + std::chrono::seconds(tvp->tv_sec) + \
std::chrono::microseconds(tvp->tv_usec);
TimedEventsManager::instance().add_event(TimedEvent(std::move(future_time),
[this]()
{
for (auto& dns_socket_handler: this->socket_handlers)
dns_socket_handler->on_recv();
},
"DNS timeout"));
}
}
#endif /* CARES_FOUND */
|