Bug 1108957 - Add per network interface dns query. This is possible only with gonk. r=mayhemer

--HG--
extra : rebase_source : 75ddd83c183db890d7c4adeb22679e7157d53372
This commit is contained in:
Dragana Damjanovic 2015-02-09 01:52:00 +01:00
parent db23797dec
commit 29e86367aa
23 changed files with 410 additions and 85 deletions

View File

@ -32,6 +32,7 @@ struct DNSCacheEntries
nsTArray<nsCString> hostaddr;
uint16_t family;
int64_t expiration;
nsCString netInterface;
};
struct HttpConnInfo

View File

@ -56,11 +56,15 @@ ChildDNSService::~ChildDNSService()
void
ChildDNSService::GetDNSRecordHashKey(const nsACString &aHost,
uint32_t aFlags,
const nsACString &aNetworkInterface,
nsIDNSListener* aListener,
nsACString &aHashKey)
{
aHashKey.Assign(aHost);
aHashKey.AppendInt(aFlags);
if (!aNetworkInterface.IsEmpty()) {
aHashKey.Append(aNetworkInterface);
}
aHashKey.AppendPrintf("%p", aListener);
}
@ -74,6 +78,18 @@ ChildDNSService::AsyncResolve(const nsACString &hostname,
nsIDNSListener *listener,
nsIEventTarget *target_,
nsICancelable **result)
{
return AsyncResolveExtended(hostname, flags, EmptyCString(), listener,
target_, result);
}
NS_IMETHODIMP
ChildDNSService::AsyncResolveExtended(const nsACString &hostname,
uint32_t flags,
const nsACString &aNetworkInterface,
nsIDNSListener *listener,
nsIEventTarget *target_,
nsICancelable **result)
{
NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
@ -108,12 +124,15 @@ ChildDNSService::AsyncResolve(const nsACString &hostname,
}
nsRefPtr<DNSRequestChild> childReq =
new DNSRequestChild(nsCString(hostname), flags, listener, target);
new DNSRequestChild(nsCString(hostname), flags,
nsCString(aNetworkInterface),
listener, target);
{
MutexAutoLock lock(mPendingRequestsLock);
nsCString key;
GetDNSRecordHashKey(hostname, originalFlags, originalListener, key);
GetDNSRecordHashKey(hostname, originalFlags, aNetworkInterface,
originalListener, key);
nsTArray<nsRefPtr<DNSRequestChild>> *hashEntry;
if (mPendingRequests.Get(key, &hashEntry)) {
hashEntry->AppendElement(childReq);
@ -135,6 +154,17 @@ ChildDNSService::CancelAsyncResolve(const nsACString &aHostname,
uint32_t aFlags,
nsIDNSListener *aListener,
nsresult aReason)
{
return CancelAsyncResolveExtended(aHostname, aFlags, EmptyCString(),
aListener, aReason);
}
NS_IMETHODIMP
ChildDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
uint32_t aFlags,
const nsACString &aNetworkInterface,
nsIDNSListener *aListener,
nsresult aReason)
{
if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) {
return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
@ -143,7 +173,7 @@ ChildDNSService::CancelAsyncResolve(const nsACString &aHostname,
MutexAutoLock lock(mPendingRequestsLock);
nsTArray<nsRefPtr<DNSRequestChild>> *hashEntry;
nsCString key;
GetDNSRecordHashKey(aHostname, aFlags, aListener, key);
GetDNSRecordHashKey(aHostname, aFlags, aNetworkInterface, aListener, key);
if (mPendingRequests.Get(key, &hashEntry)) {
// We cancel just one.
hashEntry->ElementAt(0)->Cancel(aReason);
@ -195,7 +225,8 @@ ChildDNSService::NotifyRequestDone(DNSRequestChild *aDnsRequest)
MutexAutoLock lock(mPendingRequestsLock);
nsCString key;
GetDNSRecordHashKey(aDnsRequest->mHost, originalFlags, originalListener, key);
GetDNSRecordHashKey(aDnsRequest->mHost, originalFlags,
aDnsRequest->mNetworkInterface, originalListener, key);
nsTArray<nsRefPtr<DNSRequestChild>> *hashEntry;

View File

@ -40,6 +40,7 @@ private:
void MOZ_ALWAYS_INLINE GetDNSRecordHashKey(const nsACString &aHost,
uint32_t aFlags,
const nsACString &aNetworkInterface,
nsIDNSListener* aListener,
nsACString &aHashKey);

View File

@ -172,7 +172,8 @@ public:
if (mDnsRequest->mIPCOpen) {
// Send request to Parent process.
mDnsRequest->SendCancelDNSRequest(mDnsRequest->mHost, mDnsRequest->mFlags,
mReasonForCancel);
mDnsRequest->mNetworkInterface,
mReasonForCancel);
}
return NS_OK;
}
@ -187,6 +188,7 @@ private:
DNSRequestChild::DNSRequestChild(const nsCString& aHost,
const uint32_t& aFlags,
const nsCString& aNetworkInterface,
nsIDNSListener *aListener,
nsIEventTarget *target)
: mListener(aListener)
@ -194,6 +196,7 @@ DNSRequestChild::DNSRequestChild(const nsCString& aHost,
, mResultStatus(NS_OK)
, mHost(aHost)
, mFlags(aFlags)
, mNetworkInterface(aNetworkInterface)
, mIPCOpen(false)
{
}
@ -209,7 +212,8 @@ DNSRequestChild::StartRequest()
}
// Send request to Parent process.
gNeckoChild->SendPDNSRequestConstructor(this, mHost, mFlags);
gNeckoChild->SendPDNSRequestConstructor(this, mHost, mFlags,
mNetworkInterface);
mIPCOpen = true;
// IPDL holds a reference until IPDL channel gets destroyed

View File

@ -25,6 +25,7 @@ public:
NS_DECL_NSICANCELABLE
DNSRequestChild(const nsCString& aHost, const uint32_t& aFlags,
const nsCString& aNetworkInterface,
nsIDNSListener *aListener, nsIEventTarget *target);
void AddIPDLReference() {
@ -50,6 +51,7 @@ protected:
nsresult mResultStatus;
nsCString mHost;
uint16_t mFlags;
nsCString mNetworkInterface;
bool mIPCOpen;
};

View File

@ -31,7 +31,8 @@ DNSRequestParent::~DNSRequestParent()
}
void
DNSRequestParent::DoAsyncResolve(const nsACString &hostname, uint32_t flags)
DNSRequestParent::DoAsyncResolve(const nsACString &hostname, uint32_t flags,
const nsACString &networkInterface)
{
nsresult rv;
mFlags = flags;
@ -39,8 +40,8 @@ DNSRequestParent::DoAsyncResolve(const nsACString &hostname, uint32_t flags)
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
nsCOMPtr<nsICancelable> unused;
rv = dns->AsyncResolve(hostname, flags, this, mainThread,
getter_AddRefs(unused));
rv = dns->AsyncResolveExtended(hostname, flags, networkInterface, this,
mainThread, getter_AddRefs(unused));
}
if (NS_FAILED(rv) && !mIPCClosed) {
@ -52,12 +53,14 @@ DNSRequestParent::DoAsyncResolve(const nsACString &hostname, uint32_t flags)
bool
DNSRequestParent::RecvCancelDNSRequest(const nsCString& hostName,
const uint32_t& flags,
const nsCString& networkInterface,
const nsresult& reason)
{
nsresult rv;
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
rv = dns->CancelAsyncResolve(hostName, flags, this, reason);
rv = dns->CancelAsyncResolveExtended(hostName, flags, networkInterface,
this, reason);
}
return true;
}

View File

@ -24,12 +24,14 @@ public:
DNSRequestParent();
void DoAsyncResolve(const nsACString &hostname, uint32_t flags);
void DoAsyncResolve(const nsACString &hostname, uint32_t flags,
const nsACString &networkInterface);
// Pass args here rather than storing them in the parent; they are only
// needed if the request is to be canceled.
bool RecvCancelDNSRequest(const nsCString& hostName,
const uint32_t& flags,
const nsCString& networkInterface,
const nsresult& reason) MOZ_OVERRIDE;
bool Recv__delete__() MOZ_OVERRIDE;

View File

@ -16,6 +16,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/net/DNS.h"
#include <algorithm>
#include "prerror.h"
#include "prlog.h"
#if defined(PR_LOGGING)
@ -215,13 +216,81 @@ _GetTTLData_Windows(const char* aHost, uint16_t* aResult)
}
#endif
// Make the same as nspr functions.
static MOZ_ALWAYS_INLINE PRAddrInfo*
_Android_GetAddrInfoForNetInterface(const char* hostname,
uint16_t af,
uint16_t flags,
const char* aNetworkInterface)
{
#if !defined(ANDROID) || ANDROID_VERSION < 19
PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, 0);
return nullptr;
#else
if ((af != PR_AF_INET && af != PR_AF_UNSPEC) ||
(flags & ~ PR_AI_NOCANONNAME) != PR_AI_ADDRCONFIG) {
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
return nullptr;
}
struct addrinfo *res, hints;
int rv;
memset(&hints, 0, sizeof(hints));
if (!(flags & PR_AI_NOCANONNAME)) {
hints.ai_flags |= AI_CANONNAME;
}
#ifdef AI_ADDRCONFIG
if ((flags & PR_AI_ADDRCONFIG) &&
strcmp(hostname, "localhost") != 0 &&
strcmp(hostname, "localhost.localdomain") != 0 &&
strcmp(hostname, "localhost6") != 0 &&
strcmp(hostname, "localhost6.localdomain6") != 0) {
hints.ai_flags |= AI_ADDRCONFIG;
}
#endif
hints.ai_family = (af == PR_AF_INET) ? AF_INET : AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
#if ANDROID_VERSION == 19
rv = android_getaddrinfoforiface(hostname, NULL, &hints, aNetworkInterface,
0, &res);
#else
uint32_t netId = atoi(aNetworkInterface);
rv = android_getaddrinfofornet(hostname, NULL, &hints, netId, 0, &res);
#endif
#ifdef AI_ADDRCONFIG
if (rv == EAI_BADFLAGS && (hints.ai_flags & AI_ADDRCONFIG)) {
hints.ai_flags &= ~AI_ADDRCONFIG;
#if ANDROID_VERSION == 19
rv = android_getaddrinfoforiface(hostname, NULL, &hints, aNetworkInterface,
0, &res);
#else
uint32_t netId = atoi(aNetworkInterface);
rv = android_getaddrinfofornet(hostname, NULL, &hints, netId, 0, &res);
#endif
}
#endif
if (rv == 0) {
return (PRAddrInfo *) res;
}
PR_SetError(PR_DIRECTORY_LOOKUP_ERROR, rv);
return nullptr;
#endif
}
////////////////////////////////////
// PORTABLE RUNTIME IMPLEMENTATION//
////////////////////////////////////
static MOZ_ALWAYS_INLINE nsresult
_GetAddrInfo_Portable(const char* aCanonHost, uint16_t aAddressFamily,
uint16_t aFlags, AddrInfo** aAddrInfo)
uint16_t aFlags, const char* aNetworkInterface,
AddrInfo** aAddrInfo)
{
MOZ_ASSERT(aCanonHost);
MOZ_ASSERT(aAddrInfo);
@ -241,7 +310,18 @@ _GetAddrInfo_Portable(const char* aCanonHost, uint16_t aAddressFamily,
aAddressFamily = PR_AF_UNSPEC;
}
PRAddrInfo* prai = PR_GetAddrInfoByName(aCanonHost, aAddressFamily, prFlags);
PRAddrInfo* prai;
#if defined(ANDROID) && ANDROID_VERSION >= 19
if (aNetworkInterface && aNetworkInterface[0] != '\0') {
prai = _Android_GetAddrInfoForNetInterface(aCanonHost,
aAddressFamily,
prFlags,
aNetworkInterface);
} else
#endif
{
prai = PR_GetAddrInfoByName(aCanonHost, aAddressFamily, prFlags);
}
if (!prai) {
return NS_ERROR_UNKNOWN_HOST;
@ -290,7 +370,7 @@ GetAddrInfoShutdown() {
nsresult
GetAddrInfo(const char* aHost, uint16_t aAddressFamily, uint16_t aFlags,
AddrInfo** aAddrInfo, bool aGetTtl)
const char* aNetworkInterface, AddrInfo** aAddrInfo, bool aGetTtl)
{
if (NS_WARN_IF(!aHost) || NS_WARN_IF(!aAddrInfo)) {
return NS_ERROR_NULL_POINTER;
@ -304,7 +384,8 @@ GetAddrInfo(const char* aHost, uint16_t aAddressFamily, uint16_t aFlags,
#endif
*aAddrInfo = nullptr;
nsresult rv = _GetAddrInfo_Portable(aHost, aAddressFamily, aFlags, aAddrInfo);
nsresult rv = _GetAddrInfo_Portable(aHost, aAddressFamily, aFlags,
aNetworkInterface, aAddrInfo);
#if DNSQUERY_AVAILABLE
if (aGetTtl && NS_SUCCEEDED(rv)) {

View File

@ -40,7 +40,7 @@ class AddrInfo;
*/
nsresult
GetAddrInfo(const char* aHost, uint16_t aAddressFamily, uint16_t aFlags,
AddrInfo** aAddrInfo, bool aGetTtl);
const char* aNetworkInterface, AddrInfo** aAddrInfo, bool aGetTtl);
/**
* Initialize the GetAddrInfo module.

View File

@ -23,7 +23,8 @@ parent:
// Pass args here rather than storing them in the parent; they are only
// needed if the request is to be canceled.
CancelDNSRequest(nsCString hostName, uint32_t flags, nsresult reason);
CancelDNSRequest(nsCString hostName, uint32_t flags,
nsCString networkInterface, nsresult reason);
__delete__();
child:

View File

@ -303,12 +303,14 @@ public:
const nsACString &host,
nsIDNSListener *listener,
uint16_t flags,
uint16_t af)
uint16_t af,
const nsACString &netInterface)
: mResolver(res)
, mHost(host)
, mListener(listener)
, mFlags(flags)
, mAF(af) {}
, mAF(af)
, mNetworkInterface(netInterface) {}
void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult) MOZ_OVERRIDE;
// Returns TRUE if the DNS listener arg is the same as the member listener
@ -323,6 +325,7 @@ public:
nsCOMPtr<nsIDNSListener> mListener;
uint16_t mFlags;
uint16_t mAF;
nsCString mNetworkInterface;
};
void
@ -382,7 +385,8 @@ NS_IMETHODIMP
nsDNSAsyncRequest::Cancel(nsresult reason)
{
NS_ENSURE_ARG(NS_FAILED(reason));
mResolver->DetachCallback(mHost.get(), mFlags, mAF, this, reason);
mResolver->DetachCallback(mHost.get(), mFlags, mAF, mNetworkInterface.get(),
this, reason);
return NS_OK;
}
@ -709,6 +713,18 @@ nsDNSService::AsyncResolve(const nsACString &aHostname,
nsIDNSListener *listener,
nsIEventTarget *target_,
nsICancelable **result)
{
return AsyncResolveExtended(aHostname, flags, EmptyCString(), listener, target_,
result);
}
NS_IMETHODIMP
nsDNSService::AsyncResolveExtended(const nsACString &aHostname,
uint32_t flags,
const nsACString &aNetworkInterface,
nsIDNSListener *listener,
nsIEventTarget *target_,
nsICancelable **result)
{
// grab reference to global host resolver and IDN service. beware
// simultaneous shutdown!!
@ -751,13 +767,14 @@ nsDNSService::AsyncResolve(const nsACString &aHostname,
}
if (target) {
listener = new DNSListenerProxy(listener, target);
listener = new DNSListenerProxy(listener, target);
}
uint16_t af = GetAFForLookup(hostname, flags);
nsDNSAsyncRequest *req =
new nsDNSAsyncRequest(res, hostname, listener, flags, af);
new nsDNSAsyncRequest(res, hostname, listener, flags, af,
aNetworkInterface);
if (!req)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*result = req);
@ -767,7 +784,9 @@ nsDNSService::AsyncResolve(const nsACString &aHostname,
// addref for resolver; will be released when OnLookupComplete is called.
NS_ADDREF(req);
nsresult rv = res->ResolveHost(req->mHost.get(), flags, af, req);
nsresult rv = res->ResolveHost(req->mHost.get(), flags, af,
req->mNetworkInterface.get(),
req);
if (NS_FAILED(rv)) {
NS_RELEASE(req);
NS_RELEASE(*result);
@ -780,6 +799,17 @@ nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
uint32_t aFlags,
nsIDNSListener *aListener,
nsresult aReason)
{
return CancelAsyncResolveExtended(aHostname, aFlags, EmptyCString(), aListener,
aReason);
}
NS_IMETHODIMP
nsDNSService::CancelAsyncResolveExtended(const nsACString &aHostname,
uint32_t aFlags,
const nsACString &aNetworkInterface,
nsIDNSListener *aListener,
nsresult aReason)
{
// grab reference to global host resolver and IDN service. beware
// simultaneous shutdown!!
@ -805,7 +835,9 @@ nsDNSService::CancelAsyncResolve(const nsACString &aHostname,
uint16_t af = GetAFForLookup(hostname, aFlags);
res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason);
res->CancelAsyncRequest(hostname.get(), aFlags, af,
nsPromiseFlatCString(aNetworkInterface).get(), aListener,
aReason);
return NS_OK;
}
@ -857,7 +889,7 @@ nsDNSService::Resolve(const nsACString &aHostname,
uint16_t af = GetAFForLookup(hostname, flags);
nsresult rv = res->ResolveHost(hostname.get(), flags, af, &syncReq);
nsresult rv = res->ResolveHost(hostname.get(), flags, af, "", &syncReq);
if (NS_SUCCEEDED(rv)) {
// wait for result
while (!syncReq.mDone)

View File

@ -19,6 +19,7 @@
#include "nsISupportsBase.h"
#include "nsISupportsUtils.h"
#include "nsAutoPtr.h"
#include "nsPrintfCString.h"
#include "prthread.h"
#include "prerror.h"
#include "prtime.h"
@ -75,6 +76,10 @@ static PRLogModuleInfo *gHostResolverLog = nullptr;
#define LOG(args)
#endif
#define LOG_HOST(host, interface) host, \
(interface && interface[0] != '\0') ? " on interface " : "", \
(interface && interface[0] != '\0') ? interface : ""
//----------------------------------------------------------------------------
static inline void
@ -179,7 +184,9 @@ nsHostRecord::nsHostRecord(const nsHostKey *key)
memcpy((char *) host, key->host, strlen(key->host) + 1);
flags = key->flags;
af = key->af;
netInterface = host + strlen(key->host) + 1;
memcpy((char *) netInterface, key->netInterface,
strlen(key->netInterface) + 1);
PR_INIT_CLIST(this);
PR_INIT_CLIST(&callbacks);
}
@ -188,10 +195,11 @@ nsresult
nsHostRecord::Create(const nsHostKey *key, nsHostRecord **result)
{
size_t hostLen = strlen(key->host) + 1;
size_t size = hostLen + sizeof(nsHostRecord);
size_t netInterfaceLen = strlen(key->netInterface) + 1;
size_t size = hostLen + netInterfaceLen + sizeof(nsHostRecord);
// Use placement new to create the object with room for the hostname
// allocated after it.
// Use placement new to create the object with room for the hostname and
// network interface name allocated after it.
void *place = ::operator new(size);
*result = new(place) nsHostRecord(key);
NS_ADDREF(*result);
@ -228,7 +236,8 @@ bool
nsHostRecord::Blacklisted(NetAddr *aQuery)
{
// must call locked
LOG(("Checking blacklist for host [%s], host record [%p].\n", host, this));
LOG(("Checking blacklist for host [%s%s%s], host record [%p].\n",
LOG_HOST(host, netInterface), this));
// skip the string conversion for the common case of no blacklist
if (!mBlacklistedItems.Length()) {
@ -243,7 +252,8 @@ nsHostRecord::Blacklisted(NetAddr *aQuery)
for (uint32_t i = 0; i < mBlacklistedItems.Length(); i++) {
if (mBlacklistedItems.ElementAt(i).Equals(strQuery)) {
LOG(("Address [%s] is blacklisted for host [%s].\n", buf, host));
LOG(("Address [%s] is blacklisted for host [%s%s%s].\n", buf,
LOG_HOST(host, netInterface)));
return true;
}
}
@ -255,7 +265,8 @@ void
nsHostRecord::ReportUnusable(NetAddr *aAddress)
{
// must call locked
LOG(("Adding address to blacklist for host [%s], host record [%p].\n", host, this));
LOG(("Adding address to blacklist for host [%s%s%s], host record [%p].\n",
LOG_HOST(host, netInterface), this));
++mBlacklistedCount;
@ -264,7 +275,8 @@ nsHostRecord::ReportUnusable(NetAddr *aAddress)
char buf[kIPv6CStrBufSize];
if (NetAddrToString(aAddress, buf, sizeof(buf))) {
LOG(("Successfully adding address [%s] to blacklist for host [%s].\n", buf, host));
LOG(("Successfully adding address [%s] to blacklist for host "
"[%s%s%s].\n", buf, LOG_HOST(host, netInterface)));
mBlacklistedItems.AppendElement(nsCString(buf));
}
}
@ -273,7 +285,8 @@ void
nsHostRecord::ResetBlacklist()
{
// must call locked
LOG(("Resetting blacklist for host [%s], host record [%p].\n", host, this));
LOG(("Resetting blacklist for host [%s%s%s], host record [%p].\n",
LOG_HOST(host, netInterface), this));
mBlacklistedItems.Clear();
}
@ -388,7 +401,8 @@ static PLDHashNumber
HostDB_HashKey(PLDHashTable *table, const void *key)
{
const nsHostKey *hk = static_cast<const nsHostKey *>(key);
return AddToHash(HashString(hk->host), RES_KEY_FLAGS(hk->flags), hk->af);
return AddToHash(HashString(hk->host), RES_KEY_FLAGS(hk->flags), hk->af,
HashString(hk->netInterface));
}
static bool
@ -401,7 +415,8 @@ HostDB_MatchEntry(PLDHashTable *table,
return !strcmp(he->rec->host, hk->host) &&
RES_KEY_FLAGS (he->rec->flags) == RES_KEY_FLAGS(hk->flags) &&
he->rec->af == hk->af;
he->rec->af == hk->af &&
!strcmp(he->rec->netInterface, hk->netInterface);
}
static void
@ -423,19 +438,23 @@ HostDB_ClearEntry(PLDHashTable *table,
nsHostRecord *hr = he->rec;
MOZ_ASSERT(hr, "nsHostDBEnt has null host record!");
LOG(("Clearing cache db entry for host [%s].\n", hr->host));
LOG(("Clearing cache db entry for host [%s%s%s].\n",
LOG_HOST(hr->host, hr->netInterface)));
#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));
LOG(("No address info for host [%s%s%s].\n",
LOG_HOST(hr->host, hr->netInterface)));
} else {
if (!hr->mValidEnd.IsNull()) {
TimeDuration diff = hr->mValidEnd - TimeStamp::NowLoRes();
LOG(("Record for [%s] expires in %f seconds.\n", hr->host,
LOG(("Record for host [%s%s%s] expires in %f seconds.\n",
LOG_HOST(hr->host, hr->netInterface),
diff.ToSeconds()));
} else {
LOG(("Record for [%s] not yet valid.\n", hr->host));
LOG(("Record for host [%s%s%s] not yet valid.\n",
LOG_HOST(hr->host, hr->netInterface)));
}
NetAddrElement *addrElement = nullptr;
@ -727,12 +746,14 @@ nsresult
nsHostResolver::ResolveHost(const char *host,
uint16_t flags,
uint16_t af,
const char *netInterface,
nsResolveHostCallback *callback)
{
NS_ENSURE_TRUE(host && *host, NS_ERROR_UNEXPECTED);
NS_ENSURE_TRUE(netInterface, NS_ERROR_UNEXPECTED);
LOG(("Resolving host [%s]%s.\n",
host, flags & RES_BYPASS_CACHE ? " - bypassing cache" : ""));
LOG(("Resolving host [%s%s%s]%s.\n", LOG_HOST(host, netInterface),
flags & RES_BYPASS_CACHE ? " - bypassing cache" : ""));
// ensure that we are working with a valid hostname before proceeding. see
// bug 304904 for details.
@ -762,19 +783,21 @@ nsHostResolver::ResolveHost(const char *host,
// and return. otherwise, add ourselves as first pending
// callback, and proceed to do the lookup.
nsHostKey key = { host, flags, af };
nsHostKey key = { host, flags, af, netInterface };
nsHostDBEnt *he = static_cast<nsHostDBEnt *>
(PL_DHashTableAdd(&mDB, &key, fallible));
// if the record is null, the hash table OOM'd.
if (!he) {
LOG((" Out of memory: no cache entry for [%s].\n", host));
LOG((" Out of memory: no cache entry for host [%s%s%s].\n",
LOG_HOST(host, netInterface)));
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(TimeStamp::NowLoRes(), flags)) {
LOG((" Using cached record for host [%s].\n", host));
LOG((" Using cached record for host [%s%s%s].\n",
LOG_HOST(host, netInterface)));
// put reference to host record on stack...
result = he->rec;
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2, METHOD_HIT);
@ -785,7 +808,8 @@ nsHostResolver::ResolveHost(const char *host,
ConditionallyRefreshRecord(he->rec, host);
if (he->rec->negative) {
LOG((" Negative cache entry for [%s].\n", host));
LOG((" Negative cache entry for host [%s%s%s].\n",
LOG_HOST(host, netInterface)));
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
METHOD_NEGATIVE_HIT);
status = NS_ERROR_UNKNOWN_HOST;
@ -817,15 +841,17 @@ nsHostResolver::ResolveHost(const char *host,
!IsHighPriority(flags) &&
!he->rec->resolving) {
LOG((" Lookup queue full: dropping %s priority request for "
"[%s].\n",
IsMediumPriority(flags) ? "medium" : "low", host));
"host [%s%s%s].\n",
IsMediumPriority(flags) ? "medium" : "low",
LOG_HOST(host, netInterface)));
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));
LOG((" Offline request for host [%s%s%s]; ignoring.\n",
LOG_HOST(host, netInterface)));
rv = NS_ERROR_OFFLINE;
}
@ -835,7 +861,8 @@ nsHostResolver::ResolveHost(const char *host,
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 };
const nsHostKey unspecKey = { host, flags, PR_AF_UNSPEC,
netInterface };
nsHostDBEnt *unspecHe = static_cast<nsHostDBEnt *>
(PL_DHashTableSearch(&mDB, &unspecKey));
NS_ASSERTION(!unspecHe ||
@ -847,8 +874,9 @@ nsHostResolver::ResolveHost(const char *host,
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"));
LOG((" Trying AF_UNSPEC entry for host [%s%s%s] af: %s.\n",
LOG_HOST(host, netInterface),
(af == PR_AF_INET) ? "AF_INET" : "AF_INET6"));
he->rec->addr_info = nullptr;
if (unspecHe->rec->negative) {
@ -887,7 +915,8 @@ nsHostResolver::ResolveHost(const char *host,
// negative.
else if (af == PR_AF_INET6) {
LOG((" No AF_INET6 in AF_UNSPEC entry: "
"[%s] unknown host", host));
"host [%s%s%s] unknown host.",
LOG_HOST(host, netInterface)));
result = he->rec;
he->rec->negative = true;
status = NS_ERROR_UNKNOWN_HOST;
@ -899,7 +928,9 @@ nsHostResolver::ResolveHost(const char *host,
// 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));
LOG((" No usable address in cache for host [%s%s%s].",
LOG_HOST(host, netInterface)));
// Add callback to the list of pending callbacks.
PR_APPEND_LINK(callback, &he->rec->callbacks);
he->rec->flags = flags;
@ -910,15 +941,16 @@ nsHostResolver::ResolveHost(const char *host,
PR_REMOVE_AND_INIT_LINK(callback);
}
else {
LOG((" DNS lookup for host [%s] blocking pending "
"'getaddrinfo' query: callback [%p]",
host, callback));
LOG((" DNS lookup for host [%s%s%s] blocking "
"pending 'getaddrinfo' query: callback [%p]",
LOG_HOST(host, netInterface), callback));
}
}
}
else {
LOG((" Host [%s] is being resolved. Appending callback [%p].",
host, callback));
LOG((" Host [%s%s%s] is being resolved. Appending callback "
"[%p].", LOG_HOST(host, netInterface), callback));
PR_APPEND_LINK(callback, &he->rec->callbacks);
if (he->rec->onQueue) {
Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
@ -956,6 +988,7 @@ void
nsHostResolver::DetachCallback(const char *host,
uint16_t flags,
uint16_t af,
const char *netInterface,
nsResolveHostCallback *callback,
nsresult status)
{
@ -963,7 +996,7 @@ nsHostResolver::DetachCallback(const char *host,
{
MutexAutoLock lock(mLock);
nsHostKey key = { host, flags, af };
nsHostKey key = { host, flags, af, netInterface };
nsHostDBEnt *he = static_cast<nsHostDBEnt *>
(PL_DHashTableSearch(&mDB, &key));
if (he) {
@ -1014,8 +1047,10 @@ nsHostResolver::ConditionallyCreateThread(nsHostRecord *rec)
}
}
#if defined(PR_LOGGING)
else
LOG((" Unable to find a thread for looking up host [%s].\n", rec->host));
else {
LOG((" Unable to find a thread for looking up host [%s%s%s].\n",
LOG_HOST(rec->host, rec->netInterface)));
}
#endif
return NS_OK;
}
@ -1179,7 +1214,8 @@ nsHostResolver::PrepareRecordExpiration(nsHostRecord* rec) const
if (!rec->addr_info) {
rec->SetExpiration(TimeStamp::NowLoRes(),
NEGATIVE_RECORD_LIFETIME, 0);
LOG(("Caching [%s] negative record for %u seconds.\n", rec->host,
LOG(("Caching host [%s%s%s] negative record for %u seconds.\n",
LOG_HOST(rec->host, rec->netInterface),
NEGATIVE_RECORD_LIFETIME));
return;
}
@ -1199,8 +1235,8 @@ nsHostResolver::PrepareRecordExpiration(nsHostRecord* rec) const
#endif
rec->SetExpiration(TimeStamp::NowLoRes(), lifetime, grace);
LOG(("Caching [%s] record for %u seconds (grace %d).",
rec->host, lifetime, grace));
LOG(("Caching host [%s%s%s] record for %u seconds (grace %d).",
LOG_HOST(rec->host, rec->netInterface), lifetime, grace));
}
//
@ -1271,7 +1307,8 @@ nsHostResolver::OnLookupComplete(nsHostRecord* rec, nsresult status, AddrInfo* r
}
#if TTL_AVAILABLE
if (!rec->mGetTtl && !rec->resolving && sGetTtlEnabled) {
LOG(("Issuing second async lookup for TTL for %s.", rec->host));
LOG(("Issuing second async lookup for TTL for host [%s%s%s].",
LOG_HOST(rec->host, rec->netInterface)));
rec->flags =
(rec->flags & ~RES_PRIORITY_MEDIUM) | RES_PRIORITY_LOW;
DebugOnly<nsresult> rv = IssueLookup(rec);
@ -1304,6 +1341,7 @@ void
nsHostResolver::CancelAsyncRequest(const char *host,
uint16_t flags,
uint16_t af,
const char *netInterface,
nsIDNSListener *aListener,
nsresult status)
@ -1311,7 +1349,7 @@ nsHostResolver::CancelAsyncRequest(const char *host,
MutexAutoLock lock(mLock);
// Lookup the host record associated with host, flags & address family
nsHostKey key = { host, flags, af };
nsHostKey key = { host, flags, af, netInterface };
nsHostDBEnt *he = static_cast<nsHostDBEnt *>
(PL_DHashTableSearch(&mDB, &key));
if (he) {
@ -1385,8 +1423,8 @@ nsHostResolver::ThreadFunc(void *arg)
AddrInfo *ai = nullptr;
while (rec || resolver->GetHostToLookup(&rec)) {
LOG(("DNS lookup thread - Calling getaddrinfo for host [%s].\n",
rec->host));
LOG(("DNS lookup thread - Calling getaddrinfo for host [%s%s%s].\n",
LOG_HOST(rec->host, rec->netInterface)));
TimeStamp startTime = TimeStamp::Now();
MOZ_EVENT_TRACER_EXEC(rec, "net::dns::resolve");
@ -1401,10 +1439,12 @@ nsHostResolver::ThreadFunc(void *arg)
// 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;
nsresult status = GetAddrInfo(rec->host, af, rec->flags, &ai, getTtl);
nsresult status = GetAddrInfo(rec->host, af, rec->flags, rec->netInterface,
&ai, getTtl);
#if defined(RES_RETRY_ON_FAILURE)
if (NS_FAILED(status) && rs.Reset()) {
status = GetAddrInfo(rec->host, af, rec->flags, &ai, getTtl);
status = GetAddrInfo(rec->host, af, rec->flags, rec->netInterface, &ai,
getTtl);
}
#endif
@ -1429,12 +1469,14 @@ nsHostResolver::ThreadFunc(void *arg)
}
// OnLookupComplete may release "rec", long before we lose it.
LOG(("DNS lookup thread - lookup completed for host [%s]: %s.\n",
rec->host, ai ? "success" : "failure: unknown host"));
LOG(("DNS lookup thread - lookup completed for host [%s%s%s]: %s.\n",
LOG_HOST(rec->host, rec->netInterface),
ai ? "success" : "failure: unknown host"));
if (LOOKUP_RESOLVEAGAIN == resolver->OnLookupComplete(rec, status, ai)) {
// leave 'rec' assigned and loop to make a renewed host resolve
LOG(("DNS lookup thread - Re-resolving host [%s].\n",
rec->host));
LOG(("DNS lookup thread - Re-resolving host [%s%s%s].\n",
LOG_HOST(rec->host, rec->netInterface)));
} else {
rec = nullptr;
}
@ -1483,6 +1525,7 @@ CacheEntryEnumerator(PLDHashTable *table, PLDHashEntryHdr *entry,
DNSCacheEntries info;
info.hostname = rec->host;
info.family = rec->af;
info.netInterface = rec->netInterface;
info.expiration =
(int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
if (info.expiration <= 0) {

View File

@ -37,6 +37,7 @@ struct nsHostKey
const char *host;
uint16_t flags;
uint16_t af;
const char *netInterface;
};
/**
@ -241,6 +242,7 @@ public:
nsresult ResolveHost(const char *hostname,
uint16_t flags,
uint16_t af,
const char *netInterface,
nsResolveHostCallback *callback);
/**
@ -252,6 +254,7 @@ public:
void DetachCallback(const char *hostname,
uint16_t flags,
uint16_t af,
const char *netInterface,
nsResolveHostCallback *callback,
nsresult status);
@ -265,6 +268,7 @@ public:
void CancelAsyncRequest(const char *host,
uint16_t flags,
uint16_t af,
const char *netInterface,
nsIDNSListener *aListener,
nsresult status);
/**

View File

@ -21,7 +21,7 @@ namespace mozilla { namespace net {
/**
* nsIDNSService
*/
[scriptable, uuid(f1971942-19db-44bf-81e8-d15df220a39f)]
[scriptable, uuid(de5642c6-61fc-4fcf-9a47-03226b0d4e21)]
interface nsIDNSService : nsISupports
{
/**
@ -66,7 +66,7 @@ interface nsIDNSService : nsISupports
in unsigned long aFlags,
in nsIDNSListener aListener,
in nsresult aReason);
/**
* called to synchronously resolve a hostname. warning this method may
* block the calling thread for a long period of time. it is extremely
@ -83,6 +83,31 @@ interface nsIDNSService : nsISupports
nsIDNSRecord resolve(in AUTF8String aHostName,
in unsigned long aFlags);
/**
* kicks off an asynchronous host lookup.
*
* This function is identical to asyncResolve except an additional
* parameter aNetwortInterface. If parameter aNetworkInterface is an empty
* string function will return the same result as asyncResolve.
* Setting aNetworkInterface value make only sense for gonk,because it
* an per networking interface query is possible.
*/
nsICancelable asyncResolveExtended(in AUTF8String aHostName,
in unsigned long aFlags,
in AUTF8String aNetworkInterface,
in nsIDNSListener aListener,
in nsIEventTarget aListenerTarget);
/**
* Attempts to cancel a previously requested async DNS lookup
* This is an extended versin with a additional parameter aNetworkInterface
*/
void cancelAsyncResolveExtended(in AUTF8String aHostName,
in unsigned long aFlags,
in AUTF8String aNetworkInterface,
in nsIDNSListener aListener,
in nsresult aReason);
/**
* The method takes a pointer to an nsTArray
* and fills it with cache entry data

View File

@ -254,7 +254,8 @@ NeckoChild::DeallocPUDPSocketChild(PUDPSocketChild* child)
PDNSRequestChild*
NeckoChild::AllocPDNSRequestChild(const nsCString& aHost,
const uint32_t& aFlags)
const uint32_t& aFlags,
const nsCString& aNetworkInterface)
{
// We don't allocate here: instead we always use IPDL constructor that takes
// an existing object

View File

@ -54,7 +54,8 @@ protected:
virtual PUDPSocketChild* AllocPUDPSocketChild(const nsCString& aFilter) MOZ_OVERRIDE;
virtual bool DeallocPUDPSocketChild(PUDPSocketChild*) MOZ_OVERRIDE;
virtual PDNSRequestChild* AllocPDNSRequestChild(const nsCString& aHost,
const uint32_t& aFlags) MOZ_OVERRIDE;
const uint32_t& aFlags,
const nsCString& aNetworkInterface) MOZ_OVERRIDE;
virtual bool DeallocPDNSRequestChild(PDNSRequestChild*) MOZ_OVERRIDE;
virtual PRemoteOpenFileChild*
AllocPRemoteOpenFileChild(const SerializedLoadContext& aSerialized,

View File

@ -479,7 +479,8 @@ NeckoParent::DeallocPUDPSocketParent(PUDPSocketParent* actor)
PDNSRequestParent*
NeckoParent::AllocPDNSRequestParent(const nsCString& aHost,
const uint32_t& aFlags)
const uint32_t& aFlags,
const nsCString& aNetworkInterface)
{
DNSRequestParent *p = new DNSRequestParent();
p->AddRef();
@ -489,9 +490,11 @@ NeckoParent::AllocPDNSRequestParent(const nsCString& aHost,
bool
NeckoParent::RecvPDNSRequestConstructor(PDNSRequestParent* aActor,
const nsCString& aHost,
const uint32_t& aFlags)
const uint32_t& aFlags,
const nsCString& aNetworkInterface)
{
static_cast<DNSRequestParent*>(aActor)->DoAsyncResolve(aHost, aFlags);
static_cast<DNSRequestParent*>(aActor)->DoAsyncResolve(aHost, aFlags,
aNetworkInterface);
return true;
}

View File

@ -153,10 +153,12 @@ protected:
virtual bool RecvPUDPSocketConstructor(PUDPSocketParent*, const nsCString& aFilter) MOZ_OVERRIDE;
virtual bool DeallocPUDPSocketParent(PUDPSocketParent*) MOZ_OVERRIDE;
virtual PDNSRequestParent* AllocPDNSRequestParent(const nsCString& aHost,
const uint32_t& aFlags) MOZ_OVERRIDE;
const uint32_t& aFlags,
const nsCString& aNetworkInterface) MOZ_OVERRIDE;
virtual bool RecvPDNSRequestConstructor(PDNSRequestParent* actor,
const nsCString& hostName,
const uint32_t& flags) MOZ_OVERRIDE;
const uint32_t& flags,
const nsCString& aNetworkInterface) MOZ_OVERRIDE;
virtual bool DeallocPDNSRequestParent(PDNSRequestParent*) MOZ_OVERRIDE;
virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
const uint16_t& flags) MOZ_OVERRIDE;

View File

@ -67,7 +67,7 @@ parent:
PTCPServerSocket(uint16_t localPort, uint16_t backlog, nsString binaryType);
PUDPSocket(nsCString filter);
PDNSRequest(nsCString hostName, uint32_t flags);
PDNSRequest(nsCString hostName, uint32_t flags, nsCString networkInterface);
PRemoteOpenFile(SerializedLoadContext loadContext,
URIParams fileuri,

View File

@ -0,0 +1,79 @@
var dns = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService);
// This test checks DNSService host resolver when a network interface is supplied
// as well. In the test 3 request are sent: two with a network interface set
// and one without a network interface.
// All requests have the same host to be resolved and the same flags.
// One of the request with the network interface will be canceled.
// The request with and without a network interface should not be mixed during
// the requests lifetime.
var netInterface1 = "interface1";
var netInterface2 = "interface2";
// We are not using localhost because on e10s a host resolve callback is almost
// always faster than a cancel request, therefore cancel operation would not be
// tested.
var hostname = "thisshouldnotexist.mozilla.com";
// 3 requests.
var requestWithInterfaceCanceled;
var requestWithoutInterfaceNotCanceled;
var requestWithInterfaceNotCanceled;
var listener = {
onLookupComplete: function(inRequest, inRecord, inStatus) {
// Two requests should be resolved and one request should be canceled.
// Since cancalation of a request is racy we will check only for not
// canceled request - they should not be canceled.
if ((inRequest == requestWithoutInterfaceNotCanceled) ||
(inRequest == requestWithInterfaceNotCanceled)) {
// This request should not be canceled.
do_check_neq(inStatus, Cr.NS_ERROR_ABORT);
do_test_finished();
} else if (inRequest == requestWithInterfaceCanceled) {
// We do not check the outcome for this one because it is racy -
// whether the request cancelation is faster than resolving the request.
do_test_finished();
}
},
QueryInterface: function(aIID) {
if (aIID.equals(Ci.nsIDNSListener) ||
aIID.equals(Ci.nsISupports)) {
return this;
}
throw Cr.NS_ERROR_NO_INTERFACE;
}
};
function run_test() {
var threadManager = Cc["@mozilla.org/thread-manager;1"]
.getService(Ci.nsIThreadManager);
var mainThread = threadManager.currentThread;
var flags = Ci.nsIDNSService.RESOLVE_BYPASS_CACHE;
// This one will be canceled.
requestWithInterfaceCanceled = dns.asyncResolveExtended(hostname, flags,
netInterface1,
listener,
mainThread);
requestWithInterfaceCanceled.cancel(Cr.NS_ERROR_ABORT);
// This one will not be canceled. This is the request without a network
// interface.
requestWithoutInterfaceNotCanceled = dns.asyncResolve(hostname, flags,
listener, mainThread);
// This one will not be canceled.
requestWithInterfaceNotCanceled = dns.asyncResolveExtended(hostname, flags,
netInterface2,
listener,
mainThread);
// We wait for notification for two request that are not canceled.
// For each request onLookupComplete will be called.
do_test_pending();
do_test_pending();
do_test_pending();
}

View File

@ -173,6 +173,7 @@ skip-if = bits != 32
[test_cookiejars.js]
[test_cookiejars_safebrowsing.js]
[test_dns_cancel.js]
[test_dns_per_interface.js]
[test_data_protocol.js]
[test_dns_service.js]
[test_dns_localredirect.js]

View File

@ -0,0 +1,7 @@
//
// Run test script in content process instead of chrome (xpcshell's default)
//
function run_test() {
run_test_in_child("../unit/test_dns_per_interface.js");
}

View File

@ -12,6 +12,7 @@ support-files = child_app_offline.js
[test_cookie_header_wrap.js]
[test_cookiejars_wrap.js]
[test_dns_cancel_wrap.js]
[test_dns_per_interface_wrap.js]
[test_dns_service_wrap.js]
[test_duplicate_headers_wrap.js]
[test_event_sink_wrap.js]