PLDHashOperator CacheEntryEnumerator(PLDHashTable *table, PLDHashEntryHdr *entry, uint32_t number, void *arg) { // We don't pay attention to address literals, only resolved domains. // Also require a host. nsHostRecord *rec = static_cast<nsHostDBEnt*>(entry)->rec; MOZ_ASSERT(rec, "rec should never be null here!"); if (!rec || !rec->addr_info || !rec->host) { return PL_DHASH_NEXT; } DNSCacheEntries info; info.hostname = rec->host; info.family = rec->af; info.expiration = (int64_t)(rec->expiration - TimeStamp::NowLoRes()).ToSeconds(); if (info.expiration <= 0) { // We only need valid DNS cache entries return PL_DHASH_NEXT; } { MutexAutoLock lock(rec->addr_info_lock); NetAddr *addr = nullptr; NetAddrElement *addrElement = rec->addr_info->mAddresses.getFirst(); if (addrElement) { addr = &addrElement->mAddress; } while (addr) { char buf[kIPv6CStrBufSize]; if (NetAddrToString(addr, buf, sizeof(buf))) { info.hostaddr.AppendElement(buf); } addr = nullptr; addrElement = addrElement->getNext(); if (addrElement) { addr = &addrElement->mAddress; } } } nsTArray<DNSCacheEntries> *args = static_cast<nsTArray<DNSCacheEntries> *>(arg); args->AppendElement(info); return PL_DHASH_NEXT; }
static void HostDB_ClearEntry(PLDHashTable *table, PLDHashEntryHdr *entry) { nsHostDBEnt *he = static_cast<nsHostDBEnt*>(entry); MOZ_ASSERT(he, "nsHostDBEnt is null!"); nsHostRecord *hr = he->rec; MOZ_ASSERT(hr, "nsHostDBEnt has null host record!"); LOG(("Clearing cache db entry for host [%s].\n", hr->host)); #if defined(DEBUG) && defined(PR_LOGGING) { MutexAutoLock lock(hr->addr_info_lock); if (!hr->addr_info) { LOG(("No address info for host [%s].\n", hr->host)); } else { int32_t now = (int32_t) NowInMinutes(); int32_t diff = (int32_t) hr->expiration - now; LOG(("Record for [%s] expires in %d minute(s).\n", hr->host, diff)); NetAddrElement *addrElement = nullptr; char buf[kIPv6CStrBufSize]; do { if (!addrElement) { addrElement = hr->addr_info->mAddresses.getFirst(); } else { addrElement = addrElement->getNext(); } if (addrElement) { NetAddrToString(&addrElement->mAddress, buf, sizeof(buf)); LOG((" [%s]\n", buf)); } } while (addrElement); } } #endif NS_RELEASE(he->rec); }
NS_IMETHODIMP nsDNSRecord::GetAddresses(nsTArray<NetAddr> & aAddressArray) { if (mDone) { return NS_ERROR_NOT_AVAILABLE; } mHostRecord->addr_info_lock.Lock(); if (mHostRecord->addr_info) { for (NetAddrElement *iter = mHostRecord->addr_info->mAddresses.getFirst(); iter; iter = iter->getNext()) { if (mHostRecord->Blacklisted(&iter->mAddress)) { continue; } NetAddr *addr = aAddressArray.AppendElement(NetAddr()); memcpy(addr, &iter->mAddress, sizeof(NetAddr)); if (addr->raw.family == AF_INET) { addr->inet.port = 0; } else if (addr->raw.family == AF_INET6) { addr->inet6.port = 0; } } mHostRecord->addr_info_lock.Unlock(); } else { mHostRecord->addr_info_lock.Unlock(); if (!mHostRecord->addr) { return NS_ERROR_NOT_AVAILABLE; } NetAddr *addr = aAddressArray.AppendElement(NetAddr()); memcpy(addr, mHostRecord->addr, sizeof(NetAddr)); if (addr->raw.family == AF_INET) { addr->inet.port = 0; } else if (addr->raw.family == AF_INET6) { addr->inet6.port = 0; } } return NS_OK; }
NS_IMETHODIMP nsDNSRecord::GetNextAddr(uint16_t port, NetAddr *addr) { if (mDone) { return NS_ERROR_NOT_AVAILABLE; } mHostRecord->addr_info_lock.Lock(); if (mHostRecord->addr_info) { if (mIterGenCnt != mHostRecord->addr_info_gencnt) { // mHostRecord->addr_info has changed, restart the iteration. mIter = nullptr; mIterGenCnt = mHostRecord->addr_info_gencnt; } bool startedFresh = !mIter; do { if (!mIter) { mIter = mHostRecord->addr_info->mAddresses.getFirst(); } else { mIter = mIter->getNext(); } } while (mIter && mHostRecord->Blacklisted(&mIter->mAddress)); if (!mIter && startedFresh) { // If everything was blacklisted we want to reset the blacklist (and // likely relearn it) and return the first address. That is better // than nothing. mHostRecord->ResetBlacklist(); mIter = mHostRecord->addr_info->mAddresses.getFirst(); } if (mIter) { memcpy(addr, &mIter->mAddress, sizeof(NetAddr)); } mHostRecord->addr_info_lock.Unlock(); if (!mIter) { mDone = true; return NS_ERROR_NOT_AVAILABLE; } } else { mHostRecord->addr_info_lock.Unlock(); if (!mHostRecord->addr) { // Both mHostRecord->addr_info and mHostRecord->addr are null. // This can happen if mHostRecord->addr_info expired and the // attempt to reresolve it failed. return NS_ERROR_NOT_AVAILABLE; } memcpy(addr, mHostRecord->addr, sizeof(NetAddr)); mDone = true; } // set given port port = htons(port); if (addr->raw.family == AF_INET) { addr->inet.port = port; } else if (addr->raw.family == AF_INET6) { addr->inet6.port = port; } return NS_OK; }
nsresult nsHostResolver::ResolveHost(const char *host, uint16_t flags, uint16_t af, nsResolveHostCallback *callback) { NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED); LOG(("Resolving host [%s]%s.\n", host, flags & RES_BYPASS_CACHE ? " - bypassing cache" : "")); // ensure that we are working with a valid hostname before proceeding. see // bug 304904 for details. if (!net_IsValidHostName(nsDependentCString(host))) return NS_ERROR_UNKNOWN_HOST; // if result is set inside the lock, then we need to issue the // callback before returning. nsRefPtr<nsHostRecord> result; nsresult status = NS_OK, rv = NS_OK; { MutexAutoLock lock(mLock); if (mShutdown) rv = NS_ERROR_NOT_INITIALIZED; else { // Used to try to parse to an IP address literal. PRNetAddr tempAddr; // Unfortunately, PR_StringToNetAddr does not properly initialize // the output buffer in the case of IPv6 input. See bug 223145. memset(&tempAddr, 0, sizeof(PRNetAddr)); // check to see if there is already an entry for this |host| // in the hash table. if so, then check to see if we can't // just reuse the lookup result. otherwise, if there are // any pending callbacks, then add to pending callbacks queue, // and return. otherwise, add ourselves as first pending // callback, and proceed to do the lookup. nsHostKey key = { host, flags, af }; nsHostDBEnt *he = static_cast<nsHostDBEnt *> (PL_DHashTableOperate(&mDB, &key, PL_DHASH_ADD)); // if the record is null, then HostDB_InitEntry failed. if (!he || !he->rec) { LOG((" Out of memory: no cache entry for [%s].\n", host)); rv = NS_ERROR_OUT_OF_MEMORY; } // do we have a cached result that we can reuse? else if (!(flags & RES_BYPASS_CACHE) && he->rec->HasUsableResult(flags) && TimeStamp::NowLoRes() <= (he->rec->expiration + mGracePeriod)) { LOG((" Using cached record for host [%s].\n", host)); // put reference to host record on stack... result = he->rec; Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT); // For entries that are in the grace period // or all cached negative entries, use the cache but start a new // lookup in the background ConditionallyRefreshRecord(he->rec, host); if (he->rec->negative) { LOG((" Negative cache entry for[%s].\n", host)); Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NEGATIVE_HIT); status = NS_ERROR_UNKNOWN_HOST; } } // if the host name is an IP address literal and has been parsed, // go ahead and use it. else if (he->rec->addr) { LOG((" Using cached address for IP Literal [%s].\n", host)); Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL); result = he->rec; } // try parsing the host name as an IP address literal to short // circuit full host resolution. (this is necessary on some // platforms like Win9x. see bug 219376 for more details.) else if (PR_StringToNetAddr(host, &tempAddr) == PR_SUCCESS) { LOG((" Host is IP Literal [%s].\n", host)); // ok, just copy the result into the host record, and be done // with it! ;-) he->rec->addr = new NetAddr(); PRNetAddrToNetAddr(&tempAddr, he->rec->addr); // put reference to host record on stack... Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_LITERAL); result = he->rec; } else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS && !IsHighPriority(flags) && !he->rec->resolving) { LOG((" Lookup queue full: dropping %s priority request for " "[%s].\n", IsMediumPriority(flags) ? "medium" : "low", host)); Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_OVERFLOW); // This is a lower priority request and we are swamped, so refuse it. rv = NS_ERROR_DNS_LOOKUP_QUEUE_FULL; } else if (flags & RES_OFFLINE) { LOG((" Offline request for [%s]; ignoring.\n", host)); rv = NS_ERROR_OFFLINE; } // If this is an IPV4 or IPV6 specific request, check if there is // an AF_UNSPEC entry we can use. Otherwise, hit the resolver... else if (!he->rec->resolving) { if (!(flags & RES_BYPASS_CACHE) && ((af == PR_AF_INET) || (af == PR_AF_INET6))) { // First, search for an entry with AF_UNSPEC const nsHostKey unspecKey = { host, flags, PR_AF_UNSPEC }; nsHostDBEnt *unspecHe = static_cast<nsHostDBEnt *> (PL_DHashTableOperate(&mDB, &unspecKey, PL_DHASH_LOOKUP)); NS_ASSERTION(PL_DHASH_ENTRY_IS_FREE(unspecHe) || (PL_DHASH_ENTRY_IS_BUSY(unspecHe) && unspecHe->rec), "Valid host entries should contain a record"); if (PL_DHASH_ENTRY_IS_BUSY(unspecHe) && unspecHe->rec && unspecHe->rec->HasUsableResult(flags) && TimeStamp::NowLoRes() <= (he->rec->expiration + mGracePeriod)) { MOZ_ASSERT(unspecHe->rec->addr_info || unspecHe->rec->negative, "Entry should be resolved or negative."); LOG((" Trying AF_UNSPEC entry for [%s] af: %s.\n", host, (af == PR_AF_INET) ? "AF_INET" : "AF_INET6")); he->rec->addr_info = nullptr; if (unspecHe->rec->negative) { he->rec->negative = unspecHe->rec->negative; } else if (unspecHe->rec->addr_info) { // Search for any valid address in the AF_UNSPEC entry // in the cache (not blacklisted and from the right // family). NetAddrElement *addrIter = unspecHe->rec->addr_info->mAddresses.getFirst(); while (addrIter) { if ((af == addrIter->mAddress.inet.family) && !unspecHe->rec->Blacklisted(&addrIter->mAddress)) { if (!he->rec->addr_info) { he->rec->addr_info = new AddrInfo( unspecHe->rec->addr_info->mHostName, unspecHe->rec->addr_info->mCanonicalName); } he->rec->addr_info->AddAddress( new NetAddrElement(*addrIter)); } addrIter = addrIter->getNext(); } } if (he->rec->HasUsableResult(flags)) { result = he->rec; Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT); ConditionallyRefreshRecord(he->rec, host); } // For AF_INET6, a new lookup means another AF_UNSPEC // lookup. We have already iterated through the // AF_UNSPEC addresses, so we mark this record as // negative. else if (af == PR_AF_INET6) { LOG((" No AF_INET6 in AF_UNSPEC entry: " "[%s] unknown host", host)); result = he->rec; he->rec->negative = true; status = NS_ERROR_UNKNOWN_HOST; Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NEGATIVE_HIT); } } } // If no valid address was found in the cache or this is an // AF_UNSPEC request, then start a new lookup. if (!result) { LOG((" No usable address in cache for [%s]", host)); // Add callback to the list of pending callbacks. PR_APPEND_LINK(callback, &he->rec->callbacks); he->rec->flags = flags; rv = IssueLookup(he->rec); Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NETWORK_FIRST); if (NS_FAILED(rv)) { PR_REMOVE_AND_INIT_LINK(callback); } else { LOG((" DNS lookup for host [%s] blocking pending " "'getaddrinfo' query: callback [%p]", host, callback)); } } } else { LOG((" Host [%s] is being resolved. Appending callback [%p].", host, callback)); PR_APPEND_LINK(callback, &he->rec->callbacks); if (he->rec->onQueue) { Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_NETWORK_SHARED); // Consider the case where we are on a pending queue of // lower priority than the request is being made at. // In that case we should upgrade to the higher queue. if (IsHighPriority(flags) && !IsHighPriority(he->rec->flags)) { // Move from (low|med) to high. MoveQueue(he->rec, mHighQ); he->rec->flags = flags; ConditionallyCreateThread(he->rec); } else if (IsMediumPriority(flags) && IsLowPriority(he->rec->flags)) { // Move from low to med. MoveQueue(he->rec, mMediumQ); he->rec->flags = flags; mIdleThreadCV.Notify(); } } } } } if (result) callback->OnLookupComplete(this, result, status); return rv; }