I am facing race conditions when using c-ares
to perform reverse DNS lookups in multiple threads. Code can be found below.
static void callback(void * arg, int status, int, struct hostent * host)
{
auto * ptr_records = static_cast<std::unordered_set<std::string>*>(arg);
if (ptr_records && status == ARES_SUCCESS)
{
/*
* In some cases (e.g /etc/hosts), hostent::h_name is filled and hostent::h_aliases is empty.
* Thus, we can't rely solely on hostent::h_aliases. More info on:
* https://github.com/ClickHouse/ClickHouse/issues/40595#issuecomment-1230526931
* */
if (auto * ptr_record = host->h_name)
{
ptr_records->insert(ptr_record);
}
if (host->h_aliases)
{
int i = 0;
while (auto * ptr_record = host->h_aliases[i])
{
ptr_records->insert(ptr_record);
i++;
}
}
}
}
std::unordered_set<std::string> CaresPTRResolver::resolve(const std::string & ip)
{
std::unordered_set<std::string> ptr_records;
in_addr addr;
inet_pton(AF_INET, ip.c_str(), &addr);
ares_gethostbyaddr(channel, reinterpret_cast<const void*>(&addr), sizeof(addr), AF_INET, callback, &ptr_records);
timeval * tvp, tv;
fd_set read_fds;
fd_set write_fds;
int nfds;
for (;;)
{
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
nfds = ares_fds(channel, &read_fds,&write_fds);
if (nfds == 0)
{
break;
}
tvp = ares_timeout(channel, nullptr, &tv);
select(nfds, &read_fds, &write_fds, nullptr, tvp);
ares_process(channel, &read_fds, &write_fds);
}
return ptr_records;
}
The CaresPTRResolver::resolve
method is called from multiple threads. Each thread has a different instance of CaresPTRResolver
, which means no data sharing on application side, not even the ares_channel
. If I add a mutex around ares_fds
call, the application doesn't crash anymore. This code was derived from a few examples I saw on the web, I found the docs to be very incomprehensive and lacking real examples.
The main question is: what's the proper way to use c-ares
library in a multithread environment?