From 29e86367aaa78173952b86d659dda901fe46e6b9 Mon Sep 17 00:00:00 2001
From: Dragana Damjanovic
Date: Mon, 9 Feb 2015 01:52:00 +0100
Subject: [PATCH] Bug 1108957 - Add per network interface dns query. This is
possible only with gonk. r=mayhemer
--HG--
extra : rebase_source : 75ddd83c183db890d7c4adeb22679e7157d53372
---
netwerk/base/DashboardTypes.h | 1 +
netwerk/dns/ChildDNSService.cpp | 39 ++++-
netwerk/dns/ChildDNSService.h | 1 +
netwerk/dns/DNSRequestChild.cpp | 8 +-
netwerk/dns/DNSRequestChild.h | 2 +
netwerk/dns/DNSRequestParent.cpp | 11 +-
netwerk/dns/DNSRequestParent.h | 4 +-
netwerk/dns/GetAddrInfo.cpp | 89 ++++++++++-
netwerk/dns/GetAddrInfo.h | 2 +-
netwerk/dns/PDNSRequest.ipdl | 3 +-
netwerk/dns/nsDNSService2.cpp | 48 +++++-
netwerk/dns/nsHostResolver.cpp | 143 ++++++++++++------
netwerk/dns/nsHostResolver.h | 4 +
netwerk/dns/nsIDNSService.idl | 29 +++-
netwerk/ipc/NeckoChild.cpp | 3 +-
netwerk/ipc/NeckoChild.h | 3 +-
netwerk/ipc/NeckoParent.cpp | 9 +-
netwerk/ipc/NeckoParent.h | 6 +-
netwerk/ipc/PNecko.ipdl | 2 +-
netwerk/test/unit/test_dns_per_interface.js | 79 ++++++++++
netwerk/test/unit/xpcshell.ini | 1 +
.../unit_ipc/test_dns_per_interface_wrap.js | 7 +
netwerk/test/unit_ipc/xpcshell.ini | 1 +
23 files changed, 410 insertions(+), 85 deletions(-)
create mode 100644 netwerk/test/unit/test_dns_per_interface.js
create mode 100644 netwerk/test/unit_ipc/test_dns_per_interface_wrap.js
diff --git a/netwerk/base/DashboardTypes.h b/netwerk/base/DashboardTypes.h
index bd893f5fffd..1a1e2fd5f02 100644
--- a/netwerk/base/DashboardTypes.h
+++ b/netwerk/base/DashboardTypes.h
@@ -32,6 +32,7 @@ struct DNSCacheEntries
nsTArray hostaddr;
uint16_t family;
int64_t expiration;
+ nsCString netInterface;
};
struct HttpConnInfo
diff --git a/netwerk/dns/ChildDNSService.cpp b/netwerk/dns/ChildDNSService.cpp
index 4de1ffdf7ad..6a4fc7b208a 100644
--- a/netwerk/dns/ChildDNSService.cpp
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -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 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> *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> *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> *hashEntry;
diff --git a/netwerk/dns/ChildDNSService.h b/netwerk/dns/ChildDNSService.h
index 3e04828cb74..f10a8275fec 100644
--- a/netwerk/dns/ChildDNSService.h
+++ b/netwerk/dns/ChildDNSService.h
@@ -40,6 +40,7 @@ private:
void MOZ_ALWAYS_INLINE GetDNSRecordHashKey(const nsACString &aHost,
uint32_t aFlags,
+ const nsACString &aNetworkInterface,
nsIDNSListener* aListener,
nsACString &aHashKey);
diff --git a/netwerk/dns/DNSRequestChild.cpp b/netwerk/dns/DNSRequestChild.cpp
index 45a51c11cad..85115f6c6e4 100644
--- a/netwerk/dns/DNSRequestChild.cpp
+++ b/netwerk/dns/DNSRequestChild.cpp
@@ -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
diff --git a/netwerk/dns/DNSRequestChild.h b/netwerk/dns/DNSRequestChild.h
index 579e5d0bd66..b8cab3ae2c8 100644
--- a/netwerk/dns/DNSRequestChild.h
+++ b/netwerk/dns/DNSRequestChild.h
@@ -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;
};
diff --git a/netwerk/dns/DNSRequestParent.cpp b/netwerk/dns/DNSRequestParent.cpp
index 96b29bdd891..656fabc4af4 100644
--- a/netwerk/dns/DNSRequestParent.cpp
+++ b/netwerk/dns/DNSRequestParent.cpp
@@ -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 mainThread = do_GetMainThread();
nsCOMPtr 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 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;
}
diff --git a/netwerk/dns/DNSRequestParent.h b/netwerk/dns/DNSRequestParent.h
index 0c0e4fe08a7..4a452bbb877 100644
--- a/netwerk/dns/DNSRequestParent.h
+++ b/netwerk/dns/DNSRequestParent.h
@@ -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;
diff --git a/netwerk/dns/GetAddrInfo.cpp b/netwerk/dns/GetAddrInfo.cpp
index c4aec088050..92a8b95a271 100644
--- a/netwerk/dns/GetAddrInfo.cpp
+++ b/netwerk/dns/GetAddrInfo.cpp
@@ -16,6 +16,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/net/DNS.h"
#include
+#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)) {
diff --git a/netwerk/dns/GetAddrInfo.h b/netwerk/dns/GetAddrInfo.h
index 4e26de5ef55..57c008dd506 100644
--- a/netwerk/dns/GetAddrInfo.h
+++ b/netwerk/dns/GetAddrInfo.h
@@ -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.
diff --git a/netwerk/dns/PDNSRequest.ipdl b/netwerk/dns/PDNSRequest.ipdl
index e44ece7938b..42094054546 100644
--- a/netwerk/dns/PDNSRequest.ipdl
+++ b/netwerk/dns/PDNSRequest.ipdl
@@ -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:
diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp
index 849c8bae8a9..8755c7e3268 100644
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -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 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)
diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp
index 79fa275d724..82058b2c1d3 100644
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -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(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
(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
(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
(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 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
(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) {
diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h
index 77a29b3729d..0feef651a95 100644
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -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);
/**
diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl
index 2d5a218e207..4040be74dc5 100644
--- a/netwerk/dns/nsIDNSService.idl
+++ b/netwerk/dns/nsIDNSService.idl
@@ -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
diff --git a/netwerk/ipc/NeckoChild.cpp b/netwerk/ipc/NeckoChild.cpp
index 76e72c109f2..09c0a1965c1 100644
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -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
diff --git a/netwerk/ipc/NeckoChild.h b/netwerk/ipc/NeckoChild.h
index 710675e0f71..d705db0d24e 100644
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -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,
diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp
index 8954c535302..3082a8294e0 100644
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -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(aActor)->DoAsyncResolve(aHost, aFlags);
+ static_cast(aActor)->DoAsyncResolve(aHost, aFlags,
+ aNetworkInterface);
return true;
}
diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h
index f1589616e2f..fd83b6b9e21 100644
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -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;
diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl
index 010104b1bc6..8cac2aa3196 100644
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -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,
diff --git a/netwerk/test/unit/test_dns_per_interface.js b/netwerk/test/unit/test_dns_per_interface.js
new file mode 100644
index 00000000000..508404f333a
--- /dev/null
+++ b/netwerk/test/unit/test_dns_per_interface.js
@@ -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();
+}
diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini
index 6666ca5aa58..1cbea2e3da0 100644
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -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]
diff --git a/netwerk/test/unit_ipc/test_dns_per_interface_wrap.js b/netwerk/test/unit_ipc/test_dns_per_interface_wrap.js
new file mode 100644
index 00000000000..25e7217b0e9
--- /dev/null
+++ b/netwerk/test/unit_ipc/test_dns_per_interface_wrap.js
@@ -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");
+}
diff --git a/netwerk/test/unit_ipc/xpcshell.ini b/netwerk/test/unit_ipc/xpcshell.ini
index f6ce9344023..62513ae5fdd 100644
--- a/netwerk/test/unit_ipc/xpcshell.ini
+++ b/netwerk/test/unit_ipc/xpcshell.ini
@@ -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]