Bug 820391: Use DnsQuery on Windows. r=sworkman

From 21e22e494541d5e4c085a6ba84e1bc5b4c92330e Mon Sep 17 00:00:00 2001
---
 netwerk/dns/DNS.cpp            |  65 +++++++-
 netwerk/dns/DNS.h              |   6 +
 netwerk/dns/GetAddrInfo.cpp    | 362 +++++++++++++++++++++++++++++++++++++++++
 netwerk/dns/GetAddrInfo.h      |  65 ++++++++
 netwerk/dns/moz.build          |   1 +
 netwerk/dns/nsHostResolver.cpp | 320 ++++++++++++++++++++++++++----------
 netwerk/dns/nsHostResolver.h   |  43 ++++-
 7 files changed, 766 insertions(+), 96 deletions(-)
 create mode 100644 netwerk/dns/GetAddrInfo.cpp
 create mode 100644 netwerk/dns/GetAddrInfo.h
This commit is contained in:
josullivan 2014-08-15 17:25:06 -07:00
parent 0ee11797a9
commit 4ecc0e4ae1
7 changed files with 767 additions and 97 deletions

View File

@ -255,6 +255,11 @@ NetAddrElement::NetAddrElement(const NetAddrElement& netAddr)
mAddress = netAddr.mAddress;
}
NetAddrElement::NetAddrElement(const NetAddr& aNetAddr)
{
mAddress = aNetAddr;
}
NetAddrElement::~NetAddrElement()
{
}
@ -291,6 +296,21 @@ AddrInfo::~AddrInfo()
moz_free(mCanonicalName);
}
void
AddrInfo::SetCanonicalName(const char* cname)
{
if (mCanonicalName) {
moz_free(mCanonicalName);
mCanonicalName = nullptr;
}
if (cname) {
size_t cnameLen = strlen(cname);
mCanonicalName = static_cast<char*>(moz_xmalloc(cnameLen + 1));
memcpy(mCanonicalName, cname, cnameLen + 1);
}
}
void
AddrInfo::Init(const char *host, const char *cname)
{
@ -299,14 +319,9 @@ AddrInfo::Init(const char *host, const char *cname)
size_t hostlen = strlen(host);
mHostName = static_cast<char*>(moz_xmalloc(hostlen + 1));
memcpy(mHostName, host, hostlen + 1);
if (cname) {
size_t cnameLen = strlen(cname);
mCanonicalName = static_cast<char*>(moz_xmalloc(cnameLen + 1));
memcpy(mCanonicalName, cname, cnameLen + 1);
}
else {
mCanonicalName = nullptr;
}
mCanonicalName = nullptr;
SetCanonicalName(cname);
}
void
@ -327,5 +342,39 @@ AddrInfo::SizeOfIncludingThis(MallocSizeOf mallocSizeOf) const
return n;
}
void
AddrInfo::MergeAndConsume(AddrInfo* aOther, uint16_t aAddressFamily) {
// Remove all of the addresses of the resolved type
NetAddrElement* iter = mAddresses.getFirst();
while (iter) {
NetAddrElement* next = iter->getNext();
if (iter->mAddress.raw.family == aAddressFamily) {
iter->removeFrom(mAddresses);
delete iter;
}
iter = next;
}
// Add in the new results
if (aOther) {
iter = aOther->mAddresses.getFirst();
while (iter) {
NetAddrElement* next = iter->getNext();
// Move it from one linked list to the aOther
iter->removeFrom(aOther->mAddresses);
mAddresses.insertBack(iter);
iter = next;
}
if (aOther->mCanonicalName && strlen(aOther->mCanonicalName)) {
SetCanonicalName(aOther->mCanonicalName);
}
}
}
} // namespace dns
} // namespace mozilla

View File

@ -120,6 +120,7 @@ class NetAddrElement : public LinkedListElement<NetAddrElement> {
public:
explicit NetAddrElement(const PRNetAddr *prNetAddr);
NetAddrElement(const NetAddrElement& netAddr);
NetAddrElement(const NetAddr& aNetAddr);
~NetAddrElement();
NetAddr mAddress;
@ -137,6 +138,7 @@ public:
~AddrInfo();
void AddAddress(NetAddrElement *address);
void SetCanonicalName(const char* cname);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
@ -144,6 +146,10 @@ public:
char *mCanonicalName;
LinkedList<NetAddrElement> mAddresses;
// Removes every address of addressFamily, then takes every address in other
// as well as copying its canonicalName if it has one. other may be null.
void MergeAndConsume(AddrInfo* aOther, uint16_t aAddressFamily);
private:
void Init(const char *host, const char *cname);
};

362
netwerk/dns/GetAddrInfo.cpp Normal file
View File

@ -0,0 +1,362 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(MOZ_LOGGING)
#define FORCE_PR_LOG
#endif
#include "GetAddrInfo.h"
#include "mozilla/net/DNS.h"
#include "prnetdb.h"
#include "nsHostResolver.h"
#include "nsError.h"
#include "mozilla/Mutex.h"
#include "nsAutoPtr.h"
#include "mozilla/StaticPtr.h"
#include "MainThreadUtils.h"
#include "mozilla/DebugOnly.h"
#include "prlog.h"
#if defined(PR_LOGGING)
static PRLogModuleInfo *gGetAddrInfoLog = PR_NewLogModule("GetAddrInfo");
#define LOG(msg, ...) \
PR_LOG(gGetAddrInfoLog, PR_LOG_DEBUG, ("[DNS]: " msg, ##__VA_ARGS__))
#define LOG_WARNING(msg, ...) \
PR_LOG(gGetAddrInfoLog, PR_LOG_WARNING, ("[DNS]: " msg, ##__VA_ARGS__))
#else
#define LOG(args)
#define LOG_WARNING(args)
#endif
#if DNS_API == DNS_API_WINDOWS_DNS_QUERY
// There is a bug in Windns.h where the type of parameter ppQueryResultsSet for
// DnsQuery_A is dependent on UNICODE being set. It should *always* be
// PDNS_RECORDA, but if UNICODE is set it is PDNS_RECORDW. To get around this
// we make sure that UNICODE is unset.
#undef UNICODE
#include "Windns.h"
#endif
namespace mozilla {
namespace net {
#if DNS_API == DNS_API_WINDOWS_DNS_QUERY
////////////////////////////
// WINDOWS IMPLEMENTATION //
////////////////////////////
// Ensure consistency of PR_* and AF_* constants to allow for legacy usage of
// PR_* constants with this API.
static_assert(PR_AF_INET == AF_INET && PR_AF_INET6 == AF_INET6
&& PR_AF_UNSPEC == AF_UNSPEC, "PR_AF_* must match AF_*");
// We intentionally leak this mutex. This is because we can run into a
// situation where the worker threads are still running until the process
// is actually fully shut down, and at any time one of those worker
// threads can access gDnsapiInfoLock.
static OffTheBooksMutex* gDnsapiInfoLock = nullptr;
struct DnsapiInfo
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DnsapiInfo);
HMODULE mLibrary;
decltype(&DnsQuery_A) mDnsQueryFunc;
decltype(&DnsFree) mDnsFreeFunc;
private:
// This will either be called during shutdown of the GetAddrInfo module, or
// when a worker thread is done doing a lookup (ie: within
// _GetAddrInfo_Windows). Note that the lock must be held when this is
// called.
~DnsapiInfo()
{
if (gDnsapiInfoLock) {
gDnsapiInfoLock->AssertCurrentThreadOwns();
} else {
MOZ_ASSERT_UNREACHABLE("No mutex available during GetAddrInfo shutdown.");
}
LOG("Freeing Dnsapi.dll");
MOZ_ASSERT(mLibrary);
DebugOnly<BOOL> rv = FreeLibrary(mLibrary);
NS_WARN_IF_FALSE(rv, "Failed to free Dnsapi.dll.");
}
};
static StaticRefPtr<DnsapiInfo> gDnsapiInfo;
static MOZ_ALWAYS_INLINE nsresult
_GetAddrInfoInit_Windows()
{
// This is necessary to ensure strict thread safety because if two threads
// run this function at the same time they can potentially create two
// mutexes.
MOZ_ASSERT(NS_IsMainThread(),
"Do not initialize GetAddrInfo off main thread!");
if (!gDnsapiInfoLock) {
gDnsapiInfoLock = new OffTheBooksMutex("GetAddrInfo.cpp::gDnsapiInfoLock");
}
OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
if (gDnsapiInfo) {
MOZ_ASSERT_UNREACHABLE("GetAddrInfo is being initialized multiple times!");
return NS_ERROR_ALREADY_INITIALIZED;
}
HMODULE library = LoadLibraryA("Dnsapi.dll");
if (NS_WARN_IF(!library)) {
return NS_ERROR_FAILURE;
}
FARPROC dnsQueryFunc = GetProcAddress(library, "DnsQuery_A");
FARPROC dnsFreeFunc = GetProcAddress(library, "DnsFree");
if (NS_WARN_IF(!dnsQueryFunc) || NS_WARN_IF(!dnsFreeFunc)) {
DebugOnly<BOOL> rv = FreeLibrary(library);
NS_WARN_IF_FALSE(rv, "Failed to free Dnsapi.dll.");
return NS_ERROR_FAILURE;
}
DnsapiInfo* info = new DnsapiInfo;
info->mLibrary = library;
info->mDnsQueryFunc = (decltype(info->mDnsQueryFunc)) dnsQueryFunc;
info->mDnsFreeFunc = (decltype(info->mDnsFreeFunc)) dnsFreeFunc;
gDnsapiInfo = info;
return NS_OK;
}
static MOZ_ALWAYS_INLINE nsresult
_GetAddrInfoShutdown_Windows()
{
OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
if (NS_WARN_IF(!gDnsapiInfo) || NS_WARN_IF(!gDnsapiInfoLock)) {
MOZ_ASSERT_UNREACHABLE("GetAddrInfo not initialized!");
return NS_ERROR_NOT_INITIALIZED;
}
gDnsapiInfo = nullptr;
return NS_OK;
}
static MOZ_ALWAYS_INLINE nsresult
_GetAddrInfo_Windows(const char* aHost, uint16_t aAddressFamily,
uint16_t aFlags, AddrInfo** aAddrInfo)
{
MOZ_ASSERT(aHost);
MOZ_ASSERT(aAddrInfo);
nsRefPtr<DnsapiInfo> dnsapi = nullptr;
{
OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
dnsapi = gDnsapiInfo;
}
if (!dnsapi) {
LOG_WARNING("GetAddrInfo has been shutdown or has not been initialized.");
return NS_ERROR_NOT_INITIALIZED;
}
WORD dns_type;
if (aAddressFamily == PR_AF_INET) {
dns_type = DNS_TYPE_A;
} else if (aAddressFamily == PR_AF_INET6) {
dns_type = DNS_TYPE_AAAA;
} else {
LOG_WARNING("Unsupported address family %X.\n", aAddressFamily);
MOZ_ASSERT_UNREACHABLE("Unsupported address family.");
return NS_ERROR_INVALID_ARG;
}
PDNS_RECORDA dnsData = nullptr;
DNS_STATUS status = dnsapi->mDnsQueryFunc(aHost, dns_type,
DNS_QUERY_STANDARD, nullptr, &dnsData, nullptr);
if (status == DNS_INFO_NO_RECORDS || status == DNS_ERROR_RCODE_NAME_ERROR
|| !dnsData) {
LOG("No DNS records found for %s.\n", aHost);
return NS_ERROR_UNKNOWN_HOST;
} else if (status != NOERROR) {
LOG_WARNING("DnsQuery_A failed with status %X.\n", status);
return NS_ERROR_FAILURE;
}
#ifdef PR_LOGGING
int numARecords = 0;
int numAaaaRecords = 0;
int numCnameRecords = 0;
int numUnknownRecords = 0;
#define INCREMENT_IF_LOGGING(counter) ++counter
#else
#define INCREMENT_IF_LOGGING(counter)
#endif
// Everything we need is now in dnsData. We just need to pull it out and put
// it into an AddrInfo object.
nsAutoPtr<AddrInfo> ai(new AddrInfo(aHost, nullptr));
PDNS_RECORDA curRecord = dnsData;
while (curRecord) {
if (curRecord->wType == DNS_TYPE_A) {
INCREMENT_IF_LOGGING(numARecords);
NetAddr addr;
addr.inet.family = AF_INET;
addr.inet.ip = curRecord->Data.A.IpAddress;
// Initialize port to 0 to be filled in later at socket connection time
addr.inet.port = 0;
ai->AddAddress(new NetAddrElement(addr));
} else if (curRecord->wType == DNS_TYPE_AAAA) {
INCREMENT_IF_LOGGING(numAaaaRecords);
NetAddr addr;
addr.inet6.family = AF_INET6;
memcpy(&addr.inet6.ip, &curRecord->Data.AAAA.Ip6Address, sizeof(addr.inet6.ip.u8));
// Initialize port to 0 to be filled in later at socket connection time
addr.inet6.port = 0;
ai->AddAddress(new NetAddrElement(addr));
} else if (curRecord->wType == DNS_TYPE_CNAME) {
INCREMENT_IF_LOGGING(numCnameRecords);
char* cname = curRecord->Data.CNAME.pNameHost;
LOG("Got 'CNAME' %s for %s.\n", cname, aHost);
ai->SetCanonicalName(cname);
} else {
INCREMENT_IF_LOGGING(numUnknownRecords);
LOG("Ignoring record for %s of type %X.\n", aHost, curRecord->wType);
}
curRecord = curRecord->pNext;
}
#ifdef PR_LOGGING
LOG("Got %d 'A' records, %d 'AAAA' records, %d 'CNAME' records, and %d "
"unknown records for host %s.\n", numARecords, numAaaaRecords,
numCnameRecords, numUnknownRecords, aHost);
#endif
dnsapi->mDnsFreeFunc(dnsData, DNS_FREE_TYPE::DnsFreeRecordList);
{
// dnsapi's destructor is not thread-safe, so we release explicitly here
OffTheBooksMutexAutoLock lock(*gDnsapiInfoLock);
dnsapi = nullptr;
}
if (ai->mAddresses.isEmpty()) {
return NS_ERROR_UNKNOWN_HOST;
}
*aAddrInfo = ai.forget();
return NS_OK;
}
#elif DNS_API == DNS_API_PORTABLE
////////////////////////////////////
// PORTABLE RUNTIME IMPLEMENTATION//
////////////////////////////////////
static MOZ_ALWAYS_INLINE nsresult
_GetAddrInfo_Portable(const char* aHost, uint16_t aAddressFamily,
uint16_t aFlags, AddrInfo** aAddrInfo)
{
MOZ_ASSERT(aHost);
MOZ_ASSERT(aAddrInfo);
// We accept the same aFlags that nsHostResolver::ResolveHost accepts, but we
// need to translate the aFlags into a form that PR_GetAddrInfoByName
// accepts.
int prFlags = PR_AI_ADDRCONFIG;
if (!(aFlags & nsHostResolver::RES_CANON_NAME)) {
prFlags |= PR_AI_NOCANONNAME;
}
// We need to remove IPv4 records manually because PR_GetAddrInfoByName
// doesn't support PR_AF_INET6.
bool disableIPv4 = aAddressFamily == PR_AF_INET6;
if (disableIPv4) {
aAddressFamily = PR_AF_UNSPEC;
}
PRAddrInfo* prai = PR_GetAddrInfoByName(aHost, aAddressFamily, prFlags);
if (!prai) {
return NS_ERROR_UNKNOWN_HOST;
}
const char* canonName = nullptr;
if (aFlags & nsHostResolver::RES_CANON_NAME) {
canonName = PR_GetCanonNameFromAddrInfo(prai);
}
nsAutoPtr<AddrInfo> ai(new AddrInfo(aHost, prai, disableIPv4, canonName));
PR_FreeAddrInfo(prai);
if (ai->mAddresses.isEmpty()) {
return NS_ERROR_UNKNOWN_HOST;
}
*aAddrInfo = ai.forget();
return NS_OK;
}
#endif
//////////////////////////////////////
// COMMON/PLATFORM INDEPENDENT CODE //
//////////////////////////////////////
nsresult
GetAddrInfoInit() {
LOG("Initializing GetAddrInfo.\n");
#if DNS_API == DNS_API_WINDOWS_DNS_QUERY
return _GetAddrInfoInit_Windows();
#else
return NS_OK;
#endif
}
nsresult
GetAddrInfoShutdown() {
LOG("Shutting down GetAddrInfo.\n");
#if DNS_API == DNS_API_WINDOWS_DNS_QUERY
return _GetAddrInfoShutdown_Windows();
#else
return NS_OK;
#endif
}
nsresult
GetAddrInfo(const char* aHost, uint16_t aAddressFamily, uint16_t aFlags,
AddrInfo** aAddrInfo)
{
if (NS_WARN_IF(!aHost) || NS_WARN_IF(!aAddrInfo)) {
return NS_ERROR_NULL_POINTER;
}
*aAddrInfo = nullptr;
#if DNS_API == DNS_API_WINDOWS_DNS_QUERY
return _GetAddrInfo_Windows(aHost, aAddressFamily, aFlags, aAddrInfo);
#elif DNS_API == DNS_API_PORTABLE
return _GetAddrInfo_Portable(aHost, aAddressFamily, aFlags, aAddrInfo);
#else
// This is merely to prevent programmer error in the future (setting the
// platform ID to something invalid on accident). _GetAddrInfo_Portable is
// defaulted to in the header file.
#error Unknown or unspecified DNS_API.
#endif
}
} // namespace net
} // namespace mozilla

65
netwerk/dns/GetAddrInfo.h Normal file
View File

@ -0,0 +1,65 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef netwerk_dns_GetAddrInfo_h
#define netwerk_dns_GetAddrInfo_h
#include "nsError.h"
#define DNS_API_PORTABLE (0) // Portable: PR_GetAddrInfoByName()
#define DNS_API_WINDOWS_DNS_QUERY (1) // Windows: DnsQuery()
#ifdef XP_WIN
#define DNS_API DNS_API_WINDOWS_DNS_QUERY
#else
#define DNS_API DNS_API_PORTABLE
#endif
namespace mozilla {
namespace net {
class AddrInfo;
/**
* Look up a host by name. Mostly equivalent to getaddrinfo(host, NULL, ...) of
* RFC 3493.
*
* @param hostname[in] Character string defining the host name of interest
* @param aAf[in] May be AF_INET, AF_INET6, or AF_UNSPEC. Note that AF_UNSPEC
* is not supported if DNS_API is DNS_API_WINDOWS_DNS_QUERY.
* @param aFlags[in] May be either PR_AI_ADDRCONFIG or
* PR_AI_ADDRCONFIG | PR_AI_NOCANONNAME. Include PR_AI_NOCANONNAME to
* suppress the determination of the canonical name corresponding to
* hostname.
* @param aAddrInfo[out] Will point to the results of the host lookup, or be
* null if the lookup failed.
*/
nsresult
GetAddrInfo(const char* aHost, uint16_t aAf, uint16_t aFlags, AddrInfo** aAddrInfo);
/**
* Initialize the GetAddrInfo module.
*
* GetAddrInfoShutdown() should be called for every time this function is
* called.
*/
nsresult
GetAddrInfoInit();
/**
* Shutdown the GetAddrInfo module.
*
* This function should be called for every time GetAddrInfoInit() is called.
* An assertion may throw (but is not guarenteed) if this function is called
* too many times.
*/
nsresult
GetAddrInfoShutdown();
} // namespace net
} // namespace mozilla
#endif // netwerk_dns_GetAddrInfo_h

View File

@ -25,6 +25,7 @@ EXPORTS.mozilla.net += [
]
SOURCES += [
'GetAddrInfo.cpp', # Excluded from UNIFIED_SOURCES due to NSPR forced logging.
'nsEffectiveTLDService.cpp', # Excluded from UNIFIED_SOURCES due to special build flags.
'nsHostResolver.cpp', # Excluded from UNIFIED_SOURCES due to NSPR forced logging.
]

View File

@ -10,7 +10,7 @@
#if defined(HAVE_RES_NINIT)
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <resolv.h>
#define RES_RETRY_ON_FAILURE
@ -19,6 +19,7 @@
#include <stdlib.h>
#include "nsHostResolver.h"
#include "nsError.h"
#include "GetAddrInfo.h"
#include "nsISupportsBase.h"
#include "nsISupportsUtils.h"
#include "nsAutoPtr.h"
@ -35,6 +36,7 @@
#include "mozilla/TimeStamp.h"
#include "mozilla/Telemetry.h"
#include "mozilla/VisualEventTracer.h"
#include "mozilla/DebugOnly.h"
using namespace mozilla;
using namespace mozilla::net;
@ -166,6 +168,11 @@ nsHostRecord::nsHostRecord(const nsHostKey *key)
, onQueue(false)
, usingAnyThread(false)
, mDoomed(false)
#if DO_MERGE_FOR_AF_UNSPEC
, mInnerAF(nsHostRecord::UNSPECAF_NULL)
, mCloneOf(nullptr)
, mNumPending(0)
#endif
{
host = ((char *) this) + sizeof(nsHostRecord);
memcpy((char *) host, key->host, strlen(key->host) + 1);
@ -195,6 +202,26 @@ nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result)
return NS_OK;
}
#if DO_MERGE_FOR_AF_UNSPEC
nsresult
nsHostRecord::CloneForAFUnspec(nsHostRecord** aClonedRecord, uint16_t aInnerAF)
{
nsHostRecord* cloned = nullptr;
nsresult rv = Create(static_cast<nsHostKey*>(this), &cloned);
if (NS_FAILED(rv)) {
return rv;
}
cloned->mInnerAF = aInnerAF;
cloned->mCloneOf = this;
NS_ADDREF_THIS();
*aClonedRecord = cloned;
return NS_OK;
}
#endif
nsHostRecord::~nsHostRecord()
{
delete addr_info;
@ -444,6 +471,10 @@ nsHostResolver::~nsHostResolver()
nsresult
nsHostResolver::Init()
{
if (NS_FAILED(GetAddrInfoInit())) {
return NS_ERROR_FAILURE;
}
PL_DHashTableInit(&mDB, &gHostDB_ops, nullptr, sizeof(nsHostDBEnt), 0);
mShutdown = false;
@ -534,6 +565,9 @@ nsHostResolver::Shutdown()
while (mThreadCount && PR_IntervalNow() < stopTime)
PR_Sleep(delay);
#endif
mozilla::DebugOnly<nsresult> rv = GetAddrInfoShutdown();
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to shutdown GetAddrInfo");
}
void
@ -843,7 +877,42 @@ nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
}
nsresult
nsHostResolver::IssueLookup(nsHostRecord *rec)
nsHostResolver::IssueLookup(nsHostRecord* rec)
{
#if DO_MERGE_FOR_AF_UNSPEC
// Issue two lookups to fulfill AF_UNSPEC requests: one each for AF_INET
// and AF_INET6.
if (rec->af == PR_AF_UNSPEC) {
// Enqueue a lookup for the IPv4 addresses first
rec->mInnerAF = AF_INET;
nsresult rv = IssueLookupInternal(rec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rec->mNumPending++;
// Make a clone and issue a lookup for the IPv6 addresses
nsHostRecord* rec_clone;
rv = rec->CloneForAFUnspec(&rec_clone, AF_INET6);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = IssueLookupInternal(rec_clone);
NS_RELEASE(rec_clone);
if (NS_SUCCEEDED(rv)) {
rec->mNumPending++;
}
return rv;
}
#endif
return IssueLookupInternal(rec);
}
nsresult
nsHostResolver::IssueLookupInternal(nsHostRecord* rec)
{
MOZ_EVENT_TRACER_WAIT(rec, "net::dns::resolve");
@ -853,9 +922,9 @@ nsHostResolver::IssueLookup(nsHostRecord *rec)
// Add rec to one of the pending queues, possibly removing it from mEvictionQ.
// If rec is on mEvictionQ, then we can just move the owning
// reference over to the new active queue.
if (rec->next == rec)
if (rec->next == rec) {
NS_ADDREF(rec);
else {
} else {
PR_REMOVE_LINK(rec);
mEvictionQSize--;
}
@ -978,7 +1047,7 @@ nsHostResolver::GetHostToLookup(nsHostRecord **result)
}
void
nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, AddrInfo *result)
nsHostResolver::OnLookupComplete(nsHostRecord* rec, nsresult status, AddrInfo* result)
{
// get the list of pending callbacks for this lookup, and notify
// them that the lookup is complete.
@ -987,63 +1056,156 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, AddrInfo *r
{
MutexAutoLock lock(mLock);
// grab list of callbacks to notify
MoveCList(rec->callbacks, cbs);
#if DO_MERGE_FOR_AF_UNSPEC
// If this was an unspec query, then this function will be called twice
// and we need to make sure to merge the second result with the first.
if (rec->af == PR_AF_UNSPEC) {
MOZ_ASSERT(rec->mInnerAF != nsHostRecord::UNSPECAF_NULL);
// update record fields. We might have a rec->addr_info already if a
// previous lookup result expired and we're reresolving it..
AddrInfo *old_addr_info;
{
MutexAutoLock lock(rec->addr_info_lock);
old_addr_info = rec->addr_info;
rec->addr_info = result;
rec->addr_info_gencnt++;
}
delete old_addr_info;
LOG(("OnLookupComplete: %s for UNSPEC request %s host %s.",
PR_AF_INET == rec->mInnerAF ? "INET" : "INET6",
rec->mCloneOf ? "clone" : "original",
rec->host));
rec->expiration = TimeStamp::NowLoRes();
if (result) {
rec->expiration += mMaxCacheLifetime;
rec->negative = false;
}
else {
rec->expiration += TimeDuration::FromSeconds(60); /* one minute for negative cache */
rec->negative = true;
}
rec->resolving = false;
if (rec->usingAnyThread) {
mActiveAnyThreadCount--;
rec->usingAnyThread = false;
}
nsHostRecord* originalRecord = rec->mCloneOf ? rec->mCloneOf : rec;
if (!mShutdown) {
// add to mEvictionQ
PR_APPEND_LINK(rec, &mEvictionQ);
NS_ADDREF(rec);
if (mEvictionQSize < mMaxCacheEntries)
mEvictionQSize++;
else {
// remove first element on mEvictionQ
nsHostRecord *head =
static_cast<nsHostRecord *>(PR_LIST_HEAD(&mEvictionQ));
PR_REMOVE_AND_INIT_LINK(head);
PL_DHashTableOperate(&mDB, (nsHostKey *) head, PL_DHASH_REMOVE);
{
MutexAutoLock lock(originalRecord->addr_info_lock);
if (!head->negative) {
// record the age of the entry upon eviction.
TimeDuration age = TimeStamp::NowLoRes() -
(head->expiration - mMaxCacheLifetime);
Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
static_cast<uint32_t>(age.ToSeconds() / 60));
// If we have addresses for this host already...
if (originalRecord->addr_info) {
LOG(("Merging AF_UNSPEC results into existing addr_info "
"for %s.\n", rec->host));
originalRecord->addr_info->MergeAndConsume(result,
rec->mInnerAF);
originalRecord->addr_info_gencnt++;
} else {
LOG(("Original has no addr_info, using new AF_UNSPEC "
"result for %s.\n", rec->host));
originalRecord->addr_info = result;
originalRecord->addr_info_gencnt++;
}
}
// Release the cloned record if its lookup is complete.
if (rec != originalRecord) {
MOZ_ASSERT(rec->mCloneOf);
LOG(("Deleting cloned AF_UNSPEC record for %s.\n", rec->host));
if (rec->usingAnyThread) {
mActiveAnyThreadCount--;
rec->usingAnyThread = false;
}
// release reference to rec owned by mEvictionQ
NS_RELEASE(head);
// The original record will be released at the end of this
// function, so we don't have to (and shouldn't) release it
// here.
rec->mCloneOf = nullptr;
// We need to release the clone however because it won't be
// released otherwise.
NS_RELEASE(rec);
// We're totally done with the clone now and can concern
// ourselves solely with the original record.
rec = originalRecord;
originalRecord = nullptr;
}
MOZ_ASSERT(rec->mNumPending >= 0);
rec->mNumPending--;
} else {
#else // if !DO_MERGE_FOR_AF_UNSPEC
{
#endif
LOG(("Got result for %s.\n", rec->host));
// update record fields. We might have a rec->addr_info already if
// a previous lookup result expired and we're reresolving it..
AddrInfo *old_addr_info;
{
MutexAutoLock lock(rec->addr_info_lock);
old_addr_info = rec->addr_info;
rec->addr_info = result;
rec->addr_info_gencnt++;
}
delete old_addr_info;
}
#if DO_MERGE_FOR_AF_UNSPEC
// If we're merging for AF_UNSPEC, grab the callback list only if all
// the inner lookups are complete.
if (rec->mNumPending <= 0) {
MOZ_ASSERT(rec->mNumPending == 0);
#else
// Otherwise, go ahead and grab the list of callbacks to notify.
{
#endif
MoveCList(rec->callbacks, cbs);
rec->expiration = TimeStamp::NowLoRes();
if (result) {
rec->expiration += mMaxCacheLifetime;
rec->negative = false;
}
else {
// One minute for negative cache
rec->expiration += TimeDuration::FromSeconds(60);
rec->negative = true;
}
rec->resolving = false;
if (rec->usingAnyThread) {
mActiveAnyThreadCount--;
rec->usingAnyThread = false;
}
if (!mShutdown) {
// add to mEvictionQ
PR_APPEND_LINK(rec, &mEvictionQ);
NS_ADDREF(rec);
if (mEvictionQSize < mMaxCacheEntries)
mEvictionQSize++;
else {
// remove first element on mEvictionQ
nsHostRecord *head =
static_cast<nsHostRecord *>(PR_LIST_HEAD(&mEvictionQ));
PR_REMOVE_AND_INIT_LINK(head);
PL_DHashTableOperate(&mDB, (nsHostKey *) head, PL_DHASH_REMOVE);
if (!head->negative) {
// record the age of the entry upon eviction.
TimeDuration age = TimeStamp::NowLoRes() -
(head->expiration - mMaxCacheLifetime);
Telemetry::Accumulate(Telemetry::DNS_CLEANUP_AGE,
static_cast<uint32_t>(age.ToSeconds() / 60));
}
// release reference to rec owned by mEvictionQ
NS_RELEASE(head);
}
}
}
}
#if DO_MERGE_FOR_AF_UNSPEC
// We don't want to trigger the callbacks if the inner lookups haven't
// completed yet.
if (rec->mNumPending > 0) {
NS_RELEASE(rec);
return;
}
MOZ_ASSERT(rec->mNumPending == 0);
if (rec->af == PR_AF_UNSPEC && rec->addr_info) {
MutexAutoLock lock(rec->addr_info_lock);
status = rec->addr_info->mAddresses.isEmpty() ? status : NS_OK;
}
#endif
MOZ_EVENT_TRACER_DONE(rec, "net::dns::resolve");
if (!PR_CLIST_IS_EMPTY(&cbs)) {
@ -1142,62 +1304,48 @@ nsHostResolver::ThreadFunc(void *arg)
#endif
nsHostResolver *resolver = (nsHostResolver *)arg;
nsHostRecord *rec;
PRAddrInfo *prai = nullptr;
while (resolver->GetHostToLookup(&rec)) {
LOG(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
LOG(("DNS lookup thread - Getting address info for host [%s].\n",
rec->host));
int flags = PR_AI_ADDRCONFIG;
if (!(rec->flags & RES_CANON_NAME))
flags |= PR_AI_NOCANONNAME;
TimeStamp startTime = TimeStamp::Now();
MOZ_EVENT_TRACER_EXEC(rec, "net::dns::resolve");
// We need to remove IPv4 records manually
// because PR_GetAddrInfoByName doesn't support PR_AF_INET6.
bool disableIPv4 = rec->af == PR_AF_INET6;
uint16_t af = disableIPv4 ? PR_AF_UNSPEC : rec->af;
prai = PR_GetAddrInfoByName(rec->host, af, flags);
uint16_t af;
#if DO_MERGE_FOR_AF_UNSPEC
// In the case of an unspec request, we need to make sure to use the
// "real" address family when we call GetAddrInfo.
af = rec->af == PR_AF_UNSPEC ? rec->mInnerAF : rec->af;
#else
af = rec->af;
#endif
AddrInfo* ai = nullptr;
nsresult rv = GetAddrInfo(rec->host, af, rec->flags, &ai);
#if defined(RES_RETRY_ON_FAILURE)
if (!prai && rs.Reset())
prai = PR_GetAddrInfoByName(rec->host, af, flags);
if (NS_FAILED(rv) && rs.Reset()) {
rv = GetAddrInfo(rec->host, af, rec->flags, &ai);
}
#endif
TimeDuration elapsed = TimeStamp::Now() - startTime;
uint32_t millis = static_cast<uint32_t>(elapsed.ToMilliseconds());
// convert error code to nsresult
nsresult status;
AddrInfo *ai = nullptr;
if (prai) {
const char *cname = nullptr;
if (rec->flags & RES_CANON_NAME)
cname = PR_GetCanonNameFromAddrInfo(prai);
ai = new AddrInfo(rec->host, prai, disableIPv4, cname);
PR_FreeAddrInfo(prai);
if (ai->mAddresses.isEmpty()) {
delete ai;
ai = nullptr;
}
}
if (ai) {
status = NS_OK;
if (NS_SUCCEEDED(rv)) {
MOZ_ASSERT(ai);
Telemetry::Accumulate(!rec->addr_info_gencnt ?
Telemetry::DNS_LOOKUP_TIME :
Telemetry::DNS_RENEWAL_TIME,
millis);
}
else {
status = NS_ERROR_UNKNOWN_HOST;
} else {
Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
}
// OnLookupComplete may release "rec", log before we lose it.
LOG(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
rec->host, ai ? "success" : "failure: unknown host"));
resolver->OnLookupComplete(rec, status, ai);
rec->host,
NS_SUCCEEDED(rv) ? "success" : "failure: unknown host"));
resolver->OnLookupComplete(rec, rv, ai);
}
NS_RELEASE(resolver);
LOG(("DNS lookup thread - queue empty, thread finished.\n"));

View File

@ -16,6 +16,7 @@
#include "nsIDNSListener.h"
#include "nsString.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "mozilla/net/DNS.h"
#include "mozilla/net/DashboardTypes.h"
#include "mozilla/TimeStamp.h"
@ -31,6 +32,17 @@ class nsResolveHostCallback;
#define MAX_RESOLVER_THREADS (MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY + \
MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY)
#if XP_WIN
// If this is enabled, we will make two queries to our resolver for unspec
// queries. One for any AAAA records, and the other for A records. We will then
// merge the results ourselves. This is done for us by getaddrinfo, but because
// getaddrinfo doesn't give us the TTL we use other APIs when we can that may
// not do it for us (Window's DnsQuery function for example).
#define DO_MERGE_FOR_AF_UNSPEC 1
#else
#define DO_MERGE_FOR_AF_UNSPEC 0
#endif
struct nsHostKey
{
const char *host;
@ -69,7 +81,10 @@ public:
* thread doesn't need to lock when reading |addr_info|.
*/
Mutex addr_info_lock;
int addr_info_gencnt; /* generation count of |addr_info| */
/* generation count of |addr_info|. Must be incremented whenever addr_info
* is changed so any iterators going through the old linked list can be
* invalidated. */
int addr_info_gencnt;
mozilla::net::AddrInfo *addr_info;
mozilla::net::NetAddr *addr;
bool negative; /* True if this record is a cache of a failed lookup.
@ -101,6 +116,23 @@ private:
bool usingAnyThread; /* true if off queue and contributing to mActiveAnyThreadCount */
bool mDoomed; /* explicitly expired */
#if DO_MERGE_FOR_AF_UNSPEC
// If this->af is PR_AF_UNSPEC, this will contain the address family that
// should actually be passed to GetAddrInfo.
uint16_t mInnerAF;
static const uint16_t UNSPECAF_NULL = -1;
// mCloneOf will point at the original host record if this record is a
// clone. Null otherwise.
nsHostRecord* mCloneOf;
// This will be set to the number of unresolved host records out for the
// given host record key. 0 for non AF_UNSPEC records.
int mNumPending;
nsresult CloneForAFUnspec(nsHostRecord** aNewRecord, uint16_t aUnspecAF);
#endif
// a list of addresses associated with this record that have been reported
// as unusable. the list is kept as a set of strings to make it independent
// of gencnt.
@ -244,13 +276,20 @@ private:
~nsHostResolver();
nsresult Init();
nsresult IssueLookup(nsHostRecord *);
bool GetHostToLookup(nsHostRecord **m);
void OnLookupComplete(nsHostRecord *, nsresult, mozilla::net::AddrInfo *);
void DeQueue(PRCList &aQ, nsHostRecord **aResult);
void ClearPendingQueue(PRCList *aPendingQueue);
nsresult ConditionallyCreateThread(nsHostRecord *rec);
// This will issue two lookups (using the internal version) if
// DO_MERGE_FOR_AF_UNSPEC is enabled and the passed in record is an
// AF_UNSPEC record.
nsresult IssueLookup(nsHostRecord *);
// This actually issues a single lookup
nsresult IssueLookupInternal(nsHostRecord *);
/**
* Starts a new lookup in the background for entries that are in the grace
* period with a failed connect or all cached entries are negative.