123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- #include "dnscache.h"
- #include "probes.h"
- #include "timekeeper.h"
- #include <contrib/libs/c-ares/ares.h>
- #include <util/system/guard.h>
- #include <util/datetime/systime.h>
- const TDnsCache::THost TDnsCache::NullHost;
- LWTRACE_USING(DNSCACHELIB_PROVIDER);
- static_assert(sizeof(ares_channel) == sizeof(void*), "expect sizeof(ares_channel) == sizeof(void *)");
- TDnsCache::TDnsCache(bool allowIpv4, bool allowIpv6, time_t lifetime, time_t neg, ui32 timeout)
- : EntryLifetime(lifetime)
- , NegativeLifetime(neg)
- , Timeout(TDuration::MicroSeconds(timeout))
- , AllowIpV4(allowIpv4)
- , AllowIpV6(allowIpv6)
- , ACacheHits(0)
- , ACacheMisses(0)
- , PtrCacheHits(0)
- , PtrCacheMisses(0)
- {
- #ifdef _win_
- if (ares_library_init(ARES_LIB_INIT_WIN32) != ARES_SUCCESS) {
- LWPROBE(AresInitFailed);
- ythrow yexception() << "ares_init() failed";
- }
- #endif
- ares_channel chan;
- if (ares_init(&chan) != ARES_SUCCESS) {
- LWPROBE(AresInitFailed);
- ythrow yexception() << "ares_init() failed";
- }
- Channel = chan;
- LWPROBE(Created);
- }
- TDnsCache::~TDnsCache(void) {
- ares_channel chan = static_cast<ares_channel>(Channel);
- ares_cancel(chan);
- ares_destroy(chan);
- LWPROBE(Destroyed);
- #ifdef _win_
- ares_library_cleanup();
- #endif
- }
- TString TDnsCache::GetHostByAddr(const NAddr::IRemoteAddr& addr) {
- in6_addr key;
- if (addr.Addr()->sa_family == AF_INET6) {
- const struct sockaddr_in6* s6 = (const struct sockaddr_in6*)(addr.Addr());
- memcpy(&key, &s6->sin6_addr, sizeof(s6->sin6_addr));
- } else if (addr.Addr()->sa_family == AF_INET) {
- const struct sockaddr_in* s4 = (const struct sockaddr_in*)(addr.Addr());
- memset(&key, 0, sizeof(key));
- memcpy(&key, &s4->sin_addr, sizeof(s4->sin_addr));
- } else {
- return "";
- }
- const TAddr& host = ResolveAddr(key, addr.Addr()->sa_family);
- return host.Hostname;
- }
- TIpHost TDnsCache::Get(const TString& hostname) {
- if (!AllowIpV4)
- return TIpHost(-1);
- const THost& addr = Resolve(hostname, AF_INET);
- TGuard<TMutex> lock(CacheMtx);
- if (addr.AddrsV4.empty()) {
- return TIpHost(-1);
- }
- return addr.AddrsV4.front();
- }
- NAddr::IRemoteAddrPtr TDnsCache::GetAddr(
- const TString& hostname,
- int family,
- TIpPort port,
- bool cacheOnly) {
- if (family != AF_INET && AllowIpV6) {
- const THost& addr = Resolve(hostname, AF_INET6, cacheOnly);
- TGuard<TMutex> lock(CacheMtx);
- if (!addr.AddrsV6.empty()) {
- struct sockaddr_in6 sin6;
- Zero(sin6);
- sin6.sin6_family = AF_INET6;
- sin6.sin6_addr = addr.AddrsV6.front();
- sin6.sin6_port = HostToInet(port);
- return MakeHolder<NAddr::TIPv6Addr>(sin6);
- }
- }
- if (family != AF_INET6 && AllowIpV4) {
- const THost& addr = Resolve(hostname, AF_INET, cacheOnly);
- TGuard<TMutex> lock(CacheMtx);
- if (!addr.AddrsV4.empty()) {
- return MakeHolder<NAddr::TIPv4Addr>(TIpAddress(addr.AddrsV4.front(), port));
- }
- }
- LWPROBE(FamilyMismatch, family, AllowIpV4, AllowIpV6);
- return nullptr;
- }
- void TDnsCache::GetAllAddresses(
- const TString& hostname,
- TVector<NAddr::IRemoteAddrPtr>& addrs) {
- if (AllowIpV4) {
- const THost& addr4 = Resolve(hostname, AF_INET);
- TGuard<TMutex> lock(CacheMtx);
- for (size_t i = 0; i < addr4.AddrsV4.size(); i++) {
- addrs.push_back(MakeHolder<NAddr::TIPv4Addr>(TIpAddress(addr4.AddrsV4[i], 0)));
- }
- }
- if (AllowIpV6) {
- const THost& addr6 = Resolve(hostname, AF_INET6);
- struct sockaddr_in6 sin6;
- Zero(sin6);
- sin6.sin6_family = AF_INET6;
- TGuard<TMutex> lock(CacheMtx);
- for (size_t i = 0; i < addr6.AddrsV6.size(); i++) {
- sin6.sin6_addr = addr6.AddrsV6[i];
- addrs.push_back(MakeHolder<NAddr::TIPv6Addr>(sin6));
- }
- }
- }
- void TDnsCache::GetStats(ui64& a_cache_hits, ui64& a_cache_misses,
- ui64& ptr_cache_hits, ui64& ptr_cache_misses) {
- TGuard<TMutex> lock(CacheMtx);
- a_cache_hits = ACacheHits;
- a_cache_misses = ACacheMisses;
- ptr_cache_hits = PtrCacheHits;
- ptr_cache_misses = PtrCacheMisses;
- }
- bool TDnsCache::THost::IsStale(int family, const TDnsCache* ctx) const noexcept {
- time_t resolved = family == AF_INET ? ResolvedV4 : ResolvedV6;
- time_t notfound = family == AF_INET ? NotFoundV4 : NotFoundV6;
- if (TTimeKeeper::GetTime() - resolved < ctx->EntryLifetime)
- return false;
- if (TTimeKeeper::GetTime() - notfound < ctx->NegativeLifetime)
- return false;
- return true;
- }
- const TDnsCache::THost&
- TDnsCache::Resolve(const TString& hostname, int family, bool cacheOnly) {
- if (!ValidateHName(hostname)) {
- LWPROBE(ResolveNullHost, hostname, family);
- return NullHost;
- }
- THostCache::iterator p;
- Y_ASSERT(family == AF_INET || family == AF_INET6);
- {
- TGuard<TMutex> lock(CacheMtx);
- p = HostCache.find(hostname);
- if (p != HostCache.end()) {
- if (!p->second.IsStale(family, this)) {
- /* Recently resolved, just return cached value */
- ACacheHits += 1;
- THost& host = p->second;
- LWPROBE(ResolveFromCache, hostname, family, host.AddrsV4ToString(), host.AddrsV6ToString(), ACacheHits);
- return host;
- } else {
- LWPROBE(ResolveCacheTimeout, hostname);
- }
- } else {
- /* Never resolved, create cache entry */
- LWPROBE(ResolveCacheNew, hostname);
- p = HostCache.insert(std::make_pair(hostname, THost())).first;
- }
- ACacheMisses += 1;
- }
- if (cacheOnly)
- return NullHost;
- TAtomic& inprogress = (family == AF_INET ? p->second.InProgressV4 : p->second.InProgressV6);
- {
- /* This way only! CacheMtx should always be taken AFTER AresMtx,
- * because later in ares_process it can only be done this way.
- * Lock order reversal will cause deadlock in unfortunate monents.
- */
- TGuard<TMutex> areslock(AresMtx);
- TGuard<TMutex> cachelock(CacheMtx);
- if (!inprogress) {
- ares_channel chan = static_cast<ares_channel>(Channel);
- TGHBNContext* ctx = new TGHBNContext();
- ctx->Owner = this;
- ctx->Hostname = hostname;
- ctx->Family = family;
- AtomicSet(inprogress, 1);
- ares_gethostbyname(chan, hostname.c_str(), family,
- &TDnsCache::GHBNCallback, ctx);
- }
- }
- WaitTask(inprogress);
- LWPROBE(ResolveDone, hostname, family, p->second.AddrsV4ToString(), p->second.AddrsV6ToString());
- return p->second;
- }
- bool TDnsCache::ValidateHName(const TString& name) const noexcept {
- return name.size() > 0;
- }
- const TDnsCache::TAddr& TDnsCache::ResolveAddr(const in6_addr& addr, int family) {
- TAddrCache::iterator p;
- {
- TGuard<TMutex> lock(CacheMtx);
- p = AddrCache.find(addr);
- if (p != AddrCache.end()) {
- if (TTimeKeeper::GetTime() - p->second.Resolved < EntryLifetime || TTimeKeeper::GetTime() - p->second.NotFound < NegativeLifetime) {
- /* Recently resolved, just return cached value */
- PtrCacheHits += 1;
- return p->second;
- }
- } else {
- /* Never resolved, create cache entry */
- p = AddrCache.insert(std::make_pair(addr, TAddr())).first;
- }
- PtrCacheMisses += 1;
- }
- {
- /* This way only! CacheMtx should always be taken AFTER AresMtx,
- * because later in ares_process it can only be done this way.
- * Lock order reversal will cause deadlock in unfortunate monents.
- */
- TGuard<TMutex> areslock(AresMtx);
- TGuard<TMutex> cachelock(CacheMtx);
- if (!p->second.InProgress) {
- ares_channel chan = static_cast<ares_channel>(Channel);
- TGHBAContext* ctx = new TGHBAContext();
- ctx->Owner = this;
- ctx->Addr = addr;
- AtomicSet(p->second.InProgress, 1);
- ares_gethostbyaddr(chan, &addr,
- family == AF_INET ? sizeof(in_addr) : sizeof(in6_addr),
- family, &TDnsCache::GHBACallback, ctx);
- }
- }
- WaitTask(p->second.InProgress);
- return p->second;
- }
- void TDnsCache::WaitTask(TAtomic& flag) {
- const TInstant start = TInstant(TTimeKeeper::GetTimeval());
- while (AtomicGet(flag)) {
- ares_channel chan = static_cast<ares_channel>(Channel);
- struct pollfd pfd[ARES_GETSOCK_MAXNUM];
- int nfds;
- ares_socket_t socks[ARES_GETSOCK_MAXNUM];
- int bits;
- {
- TGuard<TMutex> lock(AresMtx);
- bits = ares_getsock(chan, socks, ARES_GETSOCK_MAXNUM);
- if (bits == 0) {
- /* other thread did our job */
- continue;
- }
- }
- for (nfds = 0; nfds < ARES_GETSOCK_MAXNUM; nfds++) {
- pfd[nfds].events = 0;
- pfd[nfds].revents = 0;
- if (ARES_GETSOCK_READABLE(bits, nfds)) {
- pfd[nfds].fd = socks[nfds];
- pfd[nfds].events |= POLLRDNORM | POLLIN;
- }
- if (ARES_GETSOCK_WRITABLE(bits, nfds)) {
- pfd[nfds].fd = socks[nfds];
- pfd[nfds].events |= POLLWRNORM | POLLOUT;
- }
- if (pfd[nfds].events == 0) {
- break;
- }
- }
- Y_ASSERT(nfds != 0);
- const TDuration left = TInstant(TTimeKeeper::GetTimeval()) - start;
- const TDuration wait = Max(Timeout - left, TDuration::Zero());
- int rv = poll(pfd, nfds, wait.MilliSeconds());
- if (rv == -1) {
- if (errno == EINTR) {
- continue;
- }
- /* Unknown error in select, can't recover. Just pretend there was no reply */
- rv = 0;
- }
- if (rv == 0) {
- /* poll() timed out */
- TGuard<TMutex> lock(AresMtx);
- ares_process_fd(chan, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
- } else {
- for (int i = 0; i < nfds; i++) {
- if (pfd[i].revents == 0) {
- continue;
- }
- TGuard<TMutex> lock(AresMtx);
- ares_process_fd(chan,
- pfd[i].revents & (POLLRDNORM | POLLIN)
- ? pfd[i].fd
- : ARES_SOCKET_BAD,
- pfd[i].revents & (POLLWRNORM | POLLOUT)
- ? pfd[i].fd
- : ARES_SOCKET_BAD);
- }
- }
- if (start + Timeout <= TInstant(TTimeKeeper::GetTimeval())) {
- break;
- }
- }
- }
- void TDnsCache::GHBNCallback(void* arg, int status, int, struct hostent* info) {
- THolder<TGHBNContext> ctx(static_cast<TGHBNContext*>(arg));
- TGuard<TMutex> lock(ctx->Owner->CacheMtx);
- THostCache::iterator p = ctx->Owner->HostCache.find(ctx->Hostname);
- Y_ASSERT(p != ctx->Owner->HostCache.end());
- time_t& resolved = (ctx->Family == AF_INET ? p->second.ResolvedV4 : p->second.ResolvedV6);
- time_t& notfound = (ctx->Family == AF_INET ? p->second.NotFoundV4 : p->second.NotFoundV6);
- TAtomic& inprogress = (ctx->Family == AF_INET ? p->second.InProgressV4 : p->second.InProgressV6);
- if (status == ARES_SUCCESS) {
- if (info->h_addrtype == AF_INET) {
- p->second.AddrsV4.clear();
- for (int i = 0; info->h_addr_list[i] != nullptr; i++) {
- p->second.AddrsV4.push_back(*(TIpHost*)(info->h_addr_list[i]));
- }
- /* It is possible to ask ares for IPv6 and have IPv4 addrs instead,
- so take care and set V4 timers anyway.
- */
- p->second.ResolvedV4 = TTimeKeeper::GetTime();
- p->second.ResolvedV4 = 0;
- AtomicSet(p->second.InProgressV4, 0);
- } else if (info->h_addrtype == AF_INET6) {
- p->second.AddrsV6.clear();
- for (int i = 0; info->h_addr_list[i] != nullptr; i++) {
- p->second.AddrsV6.push_back(*(struct in6_addr*)(info->h_addr_list[i]));
- }
- } else {
- Y_FAIL("unknown address type in ares callback");
- }
- resolved = TTimeKeeper::GetTime();
- notfound = 0;
- } else {
- notfound = TTimeKeeper::GetTime();
- resolved = 0;
- }
- AtomicSet(inprogress, 0);
- }
- void TDnsCache::GHBACallback(void* arg, int status, int, struct hostent* info) {
- THolder<TGHBAContext> ctx(static_cast<TGHBAContext*>(arg));
- TGuard<TMutex> lock(ctx->Owner->CacheMtx);
- TAddrCache::iterator p = ctx->Owner->AddrCache.find(ctx->Addr);
- Y_ASSERT(p != ctx->Owner->AddrCache.end());
- if (status == ARES_SUCCESS) {
- p->second.Hostname = info->h_name;
- p->second.Resolved = TTimeKeeper::GetTime();
- p->second.NotFound = 0;
- } else {
- p->second.NotFound = TTimeKeeper::GetTime();
- p->second.Resolved = 0;
- }
- AtomicSet(p->second.InProgress, 0);
- }
- TString TDnsCache::THost::AddrsV4ToString() const {
- TStringStream ss;
- bool first = false;
- for (TIpHost addr : AddrsV4) {
- ss << (first ? "" : " ") << IpToString(addr);
- first = false;
- }
- return ss.Str();
- }
- TString TDnsCache::THost::AddrsV6ToString() const {
- TStringStream ss;
- bool first = false;
- for (in6_addr addr : AddrsV6) {
- struct sockaddr_in6 sin6;
- Zero(sin6);
- sin6.sin6_family = AF_INET6;
- sin6.sin6_addr = addr;
- NAddr::TIPv6Addr addr6(sin6);
- ss << (first ? "" : " ") << NAddr::PrintHost(addr6);
- first = false;
- }
- return ss.Str();
- }
- TDnsCache::TAresLibInit TDnsCache::InitAresLib;
|