mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
0ee11797a9
commit
4ecc0e4ae1
@ -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;
|
||||
}
|
||||
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
|
||||
|
@ -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
362
netwerk/dns/GetAddrInfo.cpp
Normal 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
65
netwerk/dns/GetAddrInfo.h
Normal 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
|
@ -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.
|
||||
]
|
||||
|
@ -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
|
||||
@ -844,6 +878,41 @@ nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
|
||||
|
||||
nsresult
|
||||
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--;
|
||||
}
|
||||
@ -987,11 +1056,74 @@ 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..
|
||||
LOG(("OnLookupComplete: %s for UNSPEC request %s host %s.",
|
||||
PR_AF_INET == rec->mInnerAF ? "INET" : "INET6",
|
||||
rec->mCloneOf ? "clone" : "original",
|
||||
rec->host));
|
||||
|
||||
nsHostRecord* originalRecord = rec->mCloneOf ? rec->mCloneOf : rec;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(originalRecord->addr_info_lock);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
@ -1000,6 +1132,18 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, AddrInfo *r
|
||||
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) {
|
||||
@ -1007,7 +1151,8 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, AddrInfo *r
|
||||
rec->negative = false;
|
||||
}
|
||||
else {
|
||||
rec->expiration += TimeDuration::FromSeconds(60); /* one minute for negative cache */
|
||||
// One minute for negative cache
|
||||
rec->expiration += TimeDuration::FromSeconds(60);
|
||||
rec->negative = true;
|
||||
}
|
||||
rec->resolving = false;
|
||||
@ -1043,6 +1188,23 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, AddrInfo *r
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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");
|
||||
|
||||
@ -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"));
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user