Bug 1108957 - Add per network interface dns query. This is possible only with gonk. r=mayhemer
☠☠ backed out by a4129403dc1c ☠ ☠
authorDragana Damjanovic <dd.mozilla@gmail.com>
Mon, 09 Feb 2015 01:52:00 +0100
changeset 242859 7d0e23391d531a9df4ddd219c381c0c4c28874de
parent 242858 cab2c930fa0223e617393cca01c14eeea8d3aab7
child 242860 7ac0b61a1b0231a2a3f721de5ceffa734e4ddbfe
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-esr52@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer
bugs1108957
milestone38.0a1
Bug 1108957 - Add per network interface dns query. This is possible only with gonk. r=mayhemer
netwerk/base/DashboardTypes.h
netwerk/dns/ChildDNSService.cpp
netwerk/dns/ChildDNSService.h
netwerk/dns/DNSRequestChild.cpp
netwerk/dns/DNSRequestChild.h
netwerk/dns/DNSRequestParent.cpp
netwerk/dns/DNSRequestParent.h
netwerk/dns/GetAddrInfo.cpp
netwerk/dns/GetAddrInfo.h
netwerk/dns/PDNSRequest.ipdl
netwerk/dns/nsDNSService2.cpp
netwerk/dns/nsHostResolver.cpp
netwerk/dns/nsHostResolver.h
netwerk/dns/nsIDNSService.idl
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/test/unit/test_dns_per_interface.js
netwerk/test/unit/xpcshell.ini
netwerk/test/unit_ipc/test_dns_per_interface_wrap.js
netwerk/test/unit_ipc/xpcshell.ini
--- a/netwerk/base/DashboardTypes.h
+++ b/netwerk/base/DashboardTypes.h
@@ -27,16 +27,17 @@ struct HalfOpenSockets
 };
 
 struct DNSCacheEntries
 {
     nsCString hostname;
     nsTArray<nsCString> hostaddr;
     uint16_t family;
     int64_t expiration;
+    nsCString netInterface;
 };
 
 struct HttpConnInfo
 {
     uint32_t ttl;
     uint32_t rtt;
     nsString protocolVersion;
 
--- a/netwerk/dns/ChildDNSService.cpp
+++ b/netwerk/dns/ChildDNSService.cpp
@@ -51,35 +51,51 @@ ChildDNSService::ChildDNSService()
 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);
 }
 
 //-----------------------------------------------------------------------------
 // ChildDNSService::nsIDNSService
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 ChildDNSService::AsyncResolve(const nsACString  &hostname,
                               uint32_t           flags,
                               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);
 
   if (mDisablePrefetch && (flags & RESOLVE_SPECULATE)) {
     return NS_ERROR_DNS_LOOKUP_QUEUE_FULL;
   }
 
   // We need original flags for the pending requests hash.
   uint32_t originalFlags = flags;
@@ -103,22 +119,25 @@ ChildDNSService::AsyncResolve(const nsAC
   }
   if (target) {
     // Guarantee listener freed on main thread.  Not sure we need this in child
     // (or in parent in nsDNSService.cpp) but doesn't hurt.
     listener = new DNSListenerProxy(listener, target);
   }
 
   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);
     } else {
       hashEntry = new nsTArray<nsRefPtr<DNSRequestChild>>();
       hashEntry->AppendElement(childReq);
       mPendingRequests.Put(key, hashEntry);
     }
@@ -131,24 +150,35 @@ ChildDNSService::AsyncResolve(const nsAC
 }
 
 NS_IMETHODIMP
 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;
   }
 
   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);
   }
 
   return NS_OK;
 }
 
@@ -190,17 +220,18 @@ ChildDNSService::NotifyRequestDone(DNSRe
       MOZ_ASSERT(originalListener);
       return;
     }
   }
 
   MutexAutoLock lock(mPendingRequestsLock);
 
   nsCString key;
-  GetDNSRecordHashKey(aDnsRequest->mHost, originalFlags, originalListener, key);
+  GetDNSRecordHashKey(aDnsRequest->mHost, originalFlags,
+                      aDnsRequest->mNetworkInterface, originalListener, key);
 
   nsTArray<nsRefPtr<DNSRequestChild>> *hashEntry;
 
   if (mPendingRequests.Get(key, &hashEntry)) {
     int idx;
     if ((idx = hashEntry->IndexOf(aDnsRequest))) {
       hashEntry->RemoveElementAt(idx);
       if (hashEntry->IsEmpty()) {
--- a/netwerk/dns/ChildDNSService.h
+++ b/netwerk/dns/ChildDNSService.h
@@ -35,16 +35,17 @@ public:
   static ChildDNSService* GetSingleton();
 
   void NotifyRequestDone(DNSRequestChild *aDnsRequest);
 private:
   virtual ~ChildDNSService();
 
   void MOZ_ALWAYS_INLINE GetDNSRecordHashKey(const nsACString &aHost,
                                              uint32_t aFlags,
+                                             const nsACString &aNetworkInterface,
                                              nsIDNSListener* aListener,
                                              nsACString &aHashKey);
 
   bool mFirstTime;
   bool mOffline;
   bool mDisablePrefetch;
 
   // We need to remember pending dns requests to be able to cancel them.
--- a/netwerk/dns/DNSRequestChild.cpp
+++ b/netwerk/dns/DNSRequestChild.cpp
@@ -167,54 +167,58 @@ public:
     , mReasonForCancel(aReason)
   {}
 
   NS_IMETHOD Run()
   {
     if (mDnsRequest->mIPCOpen) {
       // Send request to Parent process.
       mDnsRequest->SendCancelDNSRequest(mDnsRequest->mHost, mDnsRequest->mFlags,
-                                      mReasonForCancel);
+                                        mDnsRequest->mNetworkInterface,
+                                        mReasonForCancel);
     }
     return NS_OK;
   }
 private:
   nsRefPtr<DNSRequestChild> mDnsRequest;
   nsresult mReasonForCancel;
 };
 
 //-----------------------------------------------------------------------------
 // DNSRequestChild
 //-----------------------------------------------------------------------------
 
 DNSRequestChild::DNSRequestChild(const nsCString& aHost,
                                  const uint32_t& aFlags,
+                                 const nsCString& aNetworkInterface,
                                  nsIDNSListener *aListener,
                                  nsIEventTarget *target)
   : mListener(aListener)
   , mTarget(target)
   , mResultStatus(NS_OK)
   , mHost(aHost)
   , mFlags(aFlags)
+  , mNetworkInterface(aNetworkInterface)
   , mIPCOpen(false)
 {
 }
 
 void
 DNSRequestChild::StartRequest()
 {
   // we can only do IPDL on the main thread
   if (!NS_IsMainThread()) {
     NS_DispatchToMainThread(
       NS_NewRunnableMethod(this, &DNSRequestChild::StartRequest));
     return;
   }
 
   // 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
   AddIPDLReference();
 }
 
 void
 DNSRequestChild::CallOnLookupComplete()
--- a/netwerk/dns/DNSRequestChild.h
+++ b/netwerk/dns/DNSRequestChild.h
@@ -20,16 +20,17 @@ class DNSRequestChild
   : public PDNSRequestChild
   , public nsICancelable
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSICANCELABLE
 
   DNSRequestChild(const nsCString& aHost, const uint32_t& aFlags,
+                  const nsCString& aNetworkInterface,
                   nsIDNSListener *aListener, nsIEventTarget *target);
 
   void AddIPDLReference() {
     AddRef();
   }
   void ReleaseIPDLReference();
 
   // Sends IPDL request to parent
@@ -45,14 +46,15 @@ protected:
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   nsCOMPtr<nsIDNSListener>  mListener;
   nsCOMPtr<nsIEventTarget>  mTarget;
   nsCOMPtr<nsIDNSRecord>    mResultRecord;
   nsresult                  mResultStatus;
   nsCString                 mHost;
   uint16_t                  mFlags;
+  nsCString                 mNetworkInterface;
   bool                      mIPCOpen;
 };
 
 } // namespace net
 } // namespace mozilla
 #endif // mozilla_net_DNSRequestChild_h
--- a/netwerk/dns/DNSRequestParent.cpp
+++ b/netwerk/dns/DNSRequestParent.cpp
@@ -26,43 +26,46 @@ DNSRequestParent::DNSRequestParent()
 }
 
 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;
   nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
   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) {
     mIPCClosed = true;
     unused << SendLookupCompleted(DNSRequestResponse(rv));
   }
 }
 
 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;
 }
 
 bool
 DNSRequestParent::Recv__delete__()
 {
   mIPCClosed = true;
--- a/netwerk/dns/DNSRequestParent.h
+++ b/netwerk/dns/DNSRequestParent.h
@@ -19,22 +19,24 @@ class DNSRequestParent
   , public nsIDNSListener
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDNSLISTENER
 
   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;
 
 protected:
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 private:
   virtual ~DNSRequestParent();
 
--- a/netwerk/dns/GetAddrInfo.cpp
+++ b/netwerk/dns/GetAddrInfo.cpp
@@ -11,16 +11,17 @@
 #include "nsError.h"
 #include "mozilla/Mutex.h"
 #include "nsAutoPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "MainThreadUtils.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/net/DNS.h"
 #include <algorithm>
+#include "prerror.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__))
@@ -210,23 +211,91 @@ static MOZ_ALWAYS_INLINE nsresult
     return NS_ERROR_FAILURE;
   }
 
   *aResult = ttl;
   return NS_OK;
 }
 #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);
 
   // 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;
@@ -236,17 +305,28 @@ static MOZ_ALWAYS_INLINE nsresult
 
   // 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(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;
   }
 
   const char* canonName = nullptr;
   if (aFlags & nsHostResolver::RES_CANON_NAME) {
     canonName = PR_GetCanonNameFromAddrInfo(prai);
@@ -285,31 +365,32 @@ GetAddrInfoShutdown() {
   return _GetAddrInfoShutdown_Windows();
 #else
   return NS_OK;
 #endif
 }
 
 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;
   }
 
 #if DNSQUERY_AVAILABLE
   // The GetTTLData needs the canonical name to function properly
   if (aGetTtl) {
     aFlags |= nsHostResolver::RES_CANON_NAME;
   }
 #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)) {
     // Figure out the canonical name, or if that fails, just use the host name
     // we have.
     const char *name = nullptr;
     if (*aAddrInfo != nullptr && (*aAddrInfo)->mCanonicalName) {
       name = (*aAddrInfo)->mCanonicalName;
--- a/netwerk/dns/GetAddrInfo.h
+++ b/netwerk/dns/GetAddrInfo.h
@@ -35,17 +35,17 @@ class AddrInfo;
  *     hostname (PR_AI_NOCANONNAME will be ignored if the TTL is retrieved).
  * @param aAddrInfo[out] Will point to the results of the host lookup, or be
  *     null if the lookup failed.
  * @param aGetTtl[in] If true, and TTL_AVAILABLE is truthy, the TTL will be
  *     retrieved if DNS provides the answers..
  */
 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.
  *
  * GetAddrInfoShutdown() should be called for every time this function is
  * called.
  */
 nsresult
--- a/netwerk/dns/PDNSRequest.ipdl
+++ b/netwerk/dns/PDNSRequest.ipdl
@@ -18,17 +18,18 @@ async protocol PDNSRequest
 {
   manager PNecko;
 
 parent:
   // constructor in PNecko takes AsyncResolve args that initialize request
 
   // 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:
   LookupCompleted(DNSRequestResponse reply);
 
 };
 
 } //namespace net
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -298,36 +298,39 @@ class nsDNSAsyncRequest MOZ_FINAL : publ
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSICANCELABLE
 
     nsDNSAsyncRequest(nsHostResolver   *res,
                       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
     // Used in Cancellations to remove DNS requests associated with a
     // particular hostname and nsIDNSListener
     bool EqualsAsyncListener(nsIDNSListener *aListener) MOZ_OVERRIDE;
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const MOZ_OVERRIDE;
 
     nsRefPtr<nsHostResolver> mResolver;
     nsCString                mHost; // hostname we're resolving
     nsCOMPtr<nsIDNSListener> mListener;
     uint16_t                 mFlags;
     uint16_t                 mAF;
+    nsCString                mNetworkInterface;
 };
 
 void
 nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver,
                                     nsHostRecord   *hostRecord,
                                     nsresult        status)
 {
     // need to have an owning ref when we issue the callback to enable
@@ -377,17 +380,18 @@ nsDNSAsyncRequest::SizeOfIncludingThis(M
 }
 
 NS_IMPL_ISUPPORTS(nsDNSAsyncRequest, nsICancelable)
 
 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;
 }
 
 //-----------------------------------------------------------------------------
 
 class nsDNSSyncRequest : public nsResolveHostCallback
 {
 public:
@@ -705,16 +709,28 @@ static inline bool PreprocessHostname(bo
 
 NS_IMETHODIMP
 nsDNSService::AsyncResolve(const nsACString  &aHostname,
                            uint32_t           flags,
                            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!!
     nsRefPtr<nsHostResolver> res;
     nsCOMPtr<nsIIDNService> idn;
     nsCOMPtr<nsIEventTarget> target = target_;
     bool localDomain = false;
     {
         MutexAutoLock lock(mLock);
@@ -746,46 +762,60 @@ nsDNSService::AsyncResolve(const nsACStr
     nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
     if (wrappedListener && !target) {
         nsCOMPtr<nsIThread> mainThread;
         NS_GetMainThread(getter_AddRefs(mainThread));
         target = do_QueryInterface(mainThread);
     }
 
     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);
 
     MOZ_EVENT_TRACER_NAME_OBJECT(req, aHostname.BeginReading());
     MOZ_EVENT_TRACER_WAIT(req, "net::dns::lookup");
 
     // 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);
     }
     return rv;
 }
 
 NS_IMETHODIMP
 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!!
     nsRefPtr<nsHostResolver> res;
     nsCOMPtr<nsIIDNService> idn;
     bool localDomain = false;
     {
         MutexAutoLock lock(mLock);
 
@@ -800,17 +830,19 @@ nsDNSService::CancelAsyncResolve(const n
         return NS_ERROR_OFFLINE;
 
     nsCString hostname;
     if (!PreprocessHostname(localDomain, aHostname, idn, hostname))
         return NS_ERROR_FAILURE;
 
     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;
 }
 
 NS_IMETHODIMP
 nsDNSService::Resolve(const nsACString &aHostname,
                       uint32_t          flags,
                       nsIDNSRecord    **result)
 {
@@ -852,17 +884,17 @@ nsDNSService::Resolve(const nsACString &
     if (!mon)
         return NS_ERROR_OUT_OF_MEMORY;
 
     PR_EnterMonitor(mon);
     nsDNSSyncRequest syncReq(mon);
 
     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)
             PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
 
         if (NS_FAILED(syncReq.mStatus))
             rv = syncReq.mStatus;
         else {
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -14,16 +14,17 @@
 
 #include <stdlib.h>
 #include <ctime>
 #include "nsHostResolver.h"
 #include "nsError.h"
 #include "nsISupportsBase.h"
 #include "nsISupportsUtils.h"
 #include "nsAutoPtr.h"
+#include "nsPrintfCString.h"
 #include "prthread.h"
 #include "prerror.h"
 #include "prtime.h"
 #include "prlog.h"
 #include "pldhash.h"
 #include "plstr.h"
 #include "nsURLHelper.h"
 #include "nsThreadUtils.h"
@@ -70,16 +71,20 @@ PR_STATIC_ASSERT (HighThreadThreshold <=
 
 #if defined(PR_LOGGING)
 static PRLogModuleInfo *gHostResolverLog = nullptr;
 #define LOG(args) PR_LOG(gHostResolverLog, PR_LOG_DEBUG, args)
 #else
 #define LOG(args)
 #endif
 
+#define LOG_HOST(host, interface) host,                                        \
+                 (interface && interface[0] != '\0') ? " on interface " : "",  \
+                 (interface && interface[0] != '\0') ? interface : ""
+
 //----------------------------------------------------------------------------
 
 static inline void
 MoveCList(PRCList &from, PRCList &to)
 {
     if (!PR_CLIST_IS_EMPTY(&from)) {
         to.next = from.next;
         to.prev = from.prev;
@@ -174,29 +179,32 @@ nsHostRecord::nsHostRecord(const nsHostK
 #endif
     , mBlacklistedCount(0)
     , mResolveAgain(false)
 {
     host = ((char *) this) + sizeof(nsHostRecord);
     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);
 }
 
 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);
 
     MOZ_EVENT_TRACER_NAME_OBJECT(*result, key->host);
 
     return NS_OK;
 }
@@ -223,62 +231,67 @@ nsHostRecord::~nsHostRecord()
     delete addr_info;
     delete addr;
 }
 
 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()) {
         return false;
     }
 
     char buf[kIPv6CStrBufSize];
     if (!NetAddrToString(aQuery, buf, sizeof(buf))) {
         return false;
     }
     nsDependentCString strQuery(buf);
 
     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;
         }
     }
 
     return false;
 }
 
 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;
 
     if (negative)
         mDoomed = true;
 
     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));
     }
 }
 
 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();
 }
 
 nsHostRecord::ExpirationStatus
 nsHostRecord::CheckExpiration(const mozilla::TimeStamp& now) const {
     if (!mGraceStart.IsNull() && now >= mGraceStart
             && !mValidEnd.IsNull() && now < mValidEnd) {
         return nsHostRecord::EXP_GRACE;
@@ -383,30 +396,32 @@ struct nsHostDBEnt : PLDHashEntryHdr
 {
     nsHostRecord *rec;
 };
 
 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
 HostDB_MatchEntry(PLDHashTable *table,
                   const PLDHashEntryHdr *entry,
                   const void *key)
 {
     const nsHostDBEnt *he = static_cast<const nsHostDBEnt *>(entry);
     const nsHostKey *hk = static_cast<const nsHostKey *>(key); 
 
     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
 HostDB_MoveEntry(PLDHashTable *table,
                  const PLDHashEntryHdr *from,
                  PLDHashEntryHdr *to)
 {
     static_cast<nsHostDBEnt *>(to)->rec =
@@ -418,29 +433,33 @@ HostDB_ClearEntry(PLDHashTable *table,
                   PLDHashEntryHdr *entry)
 {
     nsHostDBEnt *he = static_cast<nsHostDBEnt*>(entry);
     MOZ_ASSERT(he, "nsHostDBEnt is null!");
 
     nsHostRecord *hr = he->rec;
     MOZ_ASSERT(hr, "nsHostDBEnt has null host record!");
 
-    LOG(("Clearing cache db entry for host [%s].\n", hr->host));
+    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;
             char buf[kIPv6CStrBufSize];
             do {
                 if (!addrElement) {
                     addrElement = hr->addr_info->mAddresses.getFirst();
                 } else {
@@ -722,22 +741,24 @@ nsHostResolver::MoveQueue(nsHostRecord *
     PR_REMOVE_LINK(aRec);
     PR_APPEND_LINK(aRec, &aDestQ);
 }
 
 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.
     if (!net_IsValidHostName(nsDependentCString(host)))
         return NS_ERROR_UNKNOWN_HOST;
 
     // if result is set inside the lock, then we need to issue the
     // callback before returning.
@@ -757,40 +778,43 @@ nsHostResolver::ResolveHost(const char  
             
             // check to see if there is already an entry for this |host|
             // in the hash table.  if so, then check to see if we can't
             // just reuse the lookup result.  otherwise, if there are
             // any pending callbacks, then add to pending callbacks queue,
             // and return.  otherwise, add ourselves as first pending
             // callback, and proceed to do the lookup.
 
-            nsHostKey key = { host, flags, af };
+            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);
 
                 // For entries that are in the grace period
                 // or all cached negative entries, use the cache but start a new
                 // lookup in the background
                 ConditionallyRefreshRecord(he->rec, host);
                 
                 if (he->rec->negative) {
-                    LOG(("  Negative cache entry for [%s].\n", host));
+                    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;
                 }
             }
             // if the host name is an IP address literal and has been parsed,
             // go ahead and use it.
             else if (he->rec->addr) {
@@ -812,48 +836,52 @@ nsHostResolver::ResolveHost(const char  
                 Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
                                       METHOD_LITERAL);
                 result = he->rec;
             }
             else if (mPendingCount >= MAX_NON_PRIORITY_REQUESTS &&
                      !IsHighPriority(flags) &&
                      !he->rec->resolving) {
                 LOG(("  Lookup queue full: dropping %s priority request for "
-                     "[%s].\n",
-                     IsMediumPriority(flags) ? "medium" : "low", host));
+                     "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;
             }
 
             // If this is an IPV4 or IPV6 specific request, check if there is
             // an AF_UNSPEC entry we can use. Otherwise, hit the resolver...
             else if (!he->rec->resolving) {
                 if (!(flags & RES_BYPASS_CACHE) &&
                     ((af == PR_AF_INET) || (af == PR_AF_INET6))) {
                     // First, search for an entry with AF_UNSPEC
-                    const nsHostKey unspecKey = { host, flags, PR_AF_UNSPEC };
+                    const nsHostKey unspecKey = { host, flags, PR_AF_UNSPEC,
+                                                  netInterface };
                     nsHostDBEnt *unspecHe = static_cast<nsHostDBEnt *>
                         (PL_DHashTableSearch(&mDB, &unspecKey));
                     NS_ASSERTION(!unspecHe ||
                                  (unspecHe && unspecHe->rec),
                                 "Valid host entries should contain a record");
                     if (unspecHe &&
                         unspecHe->rec->HasUsableResult(TimeStamp::NowLoRes(), flags)) {
 
                         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) {
                             he->rec->negative = unspecHe->rec->negative;
                             he->rec->CopyExpirationTimesFrom(unspecHe->rec);
                         } else if (unspecHe->rec->addr_info) {
                             // Search for any valid address in the AF_UNSPEC entry
                             // in the cache (not blacklisted and from the right
@@ -882,48 +910,52 @@ nsHostResolver::ResolveHost(const char  
                             ConditionallyRefreshRecord(he->rec, host);
                         }
                         // For AF_INET6, a new lookup means another AF_UNSPEC
                         // lookup. We have already iterated through the
                         // AF_UNSPEC addresses, so we mark this record as
                         // negative.
                         else if (af == PR_AF_INET6) {
                             LOG(("  No AF_INET6 in AF_UNSPEC entry: "
-                                 "[%s] unknown host", host));
+                                 "host [%s%s%s] unknown host.",
+                                 LOG_HOST(host, netInterface)));
                             result = he->rec;
                             he->rec->negative = true;
                             status = NS_ERROR_UNKNOWN_HOST;
                             Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
                                                   METHOD_NEGATIVE_HIT);
                         }
                     }
                 }
                 // If no valid address was found in the cache or this is an
                 // AF_UNSPEC request, then start a new lookup.
                 if (!result) {
-                    LOG(("  No usable address in cache for [%s]", host));
+                    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;
                     rv = IssueLookup(he->rec);
                     Telemetry::Accumulate(Telemetry::DNS_LOOKUP_METHOD2,
                                           METHOD_NETWORK_FIRST);
                     if (NS_FAILED(rv)) {
                         PR_REMOVE_AND_INIT_LINK(callback);
                     }
                     else {
-                        LOG(("  DNS lookup for host [%s] blocking pending "
-                             "'getaddrinfo' query: callback [%p]",
-                             host, callback));
+                        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,
                                           METHOD_NETWORK_SHARED);
 
                     // Consider the case where we are on a pending queue of
                     // lower priority than the request is being made at.
                     // In that case we should upgrade to the higher queue.
@@ -951,24 +983,25 @@ nsHostResolver::ResolveHost(const char  
 
     return rv;
 }
 
 void
 nsHostResolver::DetachCallback(const char            *host,
                                uint16_t               flags,
                                uint16_t               af,
+                               const char            *netInterface,
                                nsResolveHostCallback *callback,
                                nsresult               status)
 {
     nsRefPtr<nsHostRecord> rec;
     {
         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) {
             // walk list looking for |callback|... we cannot assume
             // that it will be there!
             PRCList *node = he->rec->callbacks.next;
             while (node != &he->rec->callbacks) {
                 if (static_cast<nsResolveHostCallback *>(node) == callback) {
@@ -1009,18 +1042,20 @@ nsHostResolver::ConditionallyCreateThrea
                                         0);
         if (!thr) {
             mThreadCount--;
             NS_RELEASE_THIS();
             return NS_ERROR_OUT_OF_MEMORY;
         }
     }
 #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;
 }
 
 nsresult
 nsHostResolver::IssueLookup(nsHostRecord *rec)
 {
     MOZ_EVENT_TRACER_WAIT(rec, "net::dns::resolve");
@@ -1174,17 +1209,18 @@ nsHostResolver::GetHostToLookup(nsHostRe
 
 void
 nsHostResolver::PrepareRecordExpiration(nsHostRecord* rec) const
 {
     MOZ_ASSERT(((bool)rec->addr_info) != rec->negative);
     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;
     }
 
     unsigned int lifetime = mDefaultCacheLifetime;
     unsigned int grace = mDefaultGracePeriod;
 #if TTL_AVAILABLE
     unsigned int ttl = mDefaultCacheLifetime;
@@ -1194,18 +1230,18 @@ nsHostResolver::PrepareRecordExpiration(
             ttl = rec->addr_info->ttl;
         }
         lifetime = ttl;
         grace = 0;
     }
 #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));
 }
 
 //
 // OnLookupComplete() checks if the resolving should be redone and if so it
 // returns LOOKUP_RESOLVEAGAIN, but only if 'status' is not NS_ERROR_ABORT.
 //
 
 nsHostResolver::LookupStatus
@@ -1266,17 +1302,18 @@ nsHostResolver::OnLookupComplete(nsHostR
                                           static_cast<uint32_t>(age.ToSeconds() / 60));
                 }
 
                 // release reference to rec owned by mEvictionQ
                 NS_RELEASE(head);
             }
 #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);
                 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                                  "Could not issue second async lookup for TTL.");
             }
 #endif
         }
@@ -1299,24 +1336,25 @@ nsHostResolver::OnLookupComplete(nsHostR
 
     return LOOKUP_OK;
 }
 
 void
 nsHostResolver::CancelAsyncRequest(const char            *host,
                                    uint16_t               flags,
                                    uint16_t               af,
+                                   const char            *netInterface,
                                    nsIDNSListener        *aListener,
                                    nsresult               status)
 
 {
     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) {
         nsHostRecord* recPtr = nullptr;
         PRCList *node = he->rec->callbacks.next;
         // Remove the first nsDNSAsyncRequest callback which matches the
         // supplied listener object
         while (node != &he->rec->callbacks) {
@@ -1380,36 +1418,38 @@ nsHostResolver::ThreadFunc(void *arg)
 #if defined(RES_RETRY_ON_FAILURE)
     nsResState rs;
 #endif
     nsHostResolver *resolver = (nsHostResolver *)arg;
     nsHostRecord *rec  = nullptr;
     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");
 
 #if TTL_AVAILABLE
         bool getTtl = rec->mGetTtl;
 #else
         bool getTtl = false;
 #endif
 
         // 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;
-        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
 
         TimeDuration elapsed = TimeStamp::Now() - startTime;
         uint32_t millis = static_cast<uint32_t>(elapsed.ToMilliseconds());
 
         if (NS_SUCCEEDED(status)) {
             Telemetry::ID histogramID;
@@ -1424,22 +1464,24 @@ nsHostResolver::ThreadFunc(void *arg)
                 histogramID = Telemetry::DNS_RENEWAL_TIME_FOR_TTL;
             }
             Telemetry::Accumulate(histogramID, millis);
         } else {
             Telemetry::Accumulate(Telemetry::DNS_FAILED_LOOKUP_TIME, millis);
         }
 
         // 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;
         }
     }
     NS_RELEASE(resolver);
     LOG(("DNS lookup thread - queue empty, thread finished.\n"));
 }
 
@@ -1478,16 +1520,17 @@ CacheEntryEnumerator(PLDHashTable *table
     MOZ_ASSERT(rec, "rec should never be null here!");
     if (!rec || !rec->addr_info || !rec->host) {
         return PL_DHASH_NEXT;
     }
 
     DNSCacheEntries info;
     info.hostname = rec->host;
     info.family = rec->af;
+    info.netInterface = rec->netInterface;
     info.expiration =
         (int64_t)(rec->mValidEnd - TimeStamp::NowLoRes()).ToSeconds();
     if (info.expiration <= 0) {
         // We only need valid DNS cache entries
         return PL_DHASH_NEXT;
     }
 
     {
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -32,16 +32,17 @@ class nsResolveHostCallback;
 #define MAX_RESOLVER_THREADS (MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY + \
                               MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY)
 
 struct nsHostKey
 {
     const char *host;
     uint16_t    flags;
     uint16_t    af;
+    const char *netInterface;
 };
 
 /**
  * nsHostRecord - ref counted object type stored in host resolver cache.
  */
 class nsHostRecord : public PRCList, public nsHostKey
 {
     typedef mozilla::Mutex Mutex;
@@ -236,40 +237,43 @@ public:
      * a synchronous host lookup using a lock and a cvar.  as noted above
      * the callback will occur re-entrantly from an unspecified thread.  the
      * host lookup cannot be canceled (cancelation can be layered above this
      * by having the callback implementation return without doing anything).
      */
     nsresult ResolveHost(const char            *hostname,
                          uint16_t               flags,
                          uint16_t               af,
+                         const char            *netInterface,
                          nsResolveHostCallback *callback);
 
     /**
      * removes the specified callback from the nsHostRecord for the given
      * hostname, flags, and address family.  these parameters should correspond
      * to the parameters passed to ResolveHost.  this function executes the
      * callback if the callback is still pending with the given status.
      */
     void DetachCallback(const char            *hostname,
                         uint16_t               flags,
                         uint16_t               af,
+                        const char            *netInterface,
                         nsResolveHostCallback *callback,
                         nsresult               status);
 
     /**
      * Cancels an async request associated with the hostname, flags,
      * address family and listener.  Cancels first callback found which matches
      * these criteria.  These parameters should correspond to the parameters
      * passed to ResolveHost.  If this is the last callback associated with the
      * host record, it is removed from any request queues it might be on. 
      */
     void CancelAsyncRequest(const char            *host,
                             uint16_t               flags,
                             uint16_t               af,
+                            const char            *netInterface,
                             nsIDNSListener        *aListener,
                             nsresult               status);
     /**
      * values for the flags parameter passed to ResolveHost and DetachCallback
      * that may be bitwise OR'd together.
      *
      * NOTE: in this implementation, these flags correspond exactly in value
      *       to the flags defined on nsIDNSService.
--- a/netwerk/dns/nsIDNSService.idl
+++ b/netwerk/dns/nsIDNSService.idl
@@ -16,17 +16,17 @@ namespace mozilla { namespace net {
 } }
 %}
 
 [ptr] native EntriesArray(nsTArray<mozilla::net::DNSCacheEntries>);
 
 /**
  * nsIDNSService
  */
-[scriptable, uuid(f1971942-19db-44bf-81e8-d15df220a39f)]
+[scriptable, uuid(de5642c6-61fc-4fcf-9a47-03226b0d4e21)]
 interface nsIDNSService : nsISupports
 {
     /**
      * kicks off an asynchronous host lookup.
      *
      * @param aHostName
      *        the hostname or IP-address-literal to resolve.
      * @param aFlags
@@ -61,17 +61,17 @@ interface nsIDNSService : nsISupports
      *        nsresult reason for the cancellation
      *
      * @return An object that can be used to cancel the host lookup.
      */
     void cancelAsyncResolve(in AUTF8String       aHostName,
                             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
      * unwise to call this function on the UI thread of an application.
      *
      * @param aHostName
      *        the hostname or IP-address-literal to resolve.
      * @param aFlags
@@ -79,16 +79,41 @@ interface nsIDNSService : nsISupports
      *
      * @return DNS record corresponding to the given hostname.
      * @throws NS_ERROR_UNKNOWN_HOST if host could not be resolved.
      */
     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
      * Called by the networking dashboard
      */
     [noscript] void getDNSCacheEntries(in EntriesArray args);
 
     /**
      * @return the hostname of the operating system.
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -249,17 +249,18 @@ NeckoChild::DeallocPUDPSocketChild(PUDPS
 
   UDPSocketChild* p = static_cast<UDPSocketChild*>(child);
   p->ReleaseIPDLReference();
   return true;
 }
 
 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
   NS_NOTREACHED("AllocPDNSRequestChild should not be called on child");
   return nullptr;
 }
 
 bool
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -49,17 +49,18 @@ protected:
   virtual PTCPServerSocketChild*
     AllocPTCPServerSocketChild(const uint16_t& aLocalPort,
                                const uint16_t& aBacklog,
                                const nsString& aBinaryType) MOZ_OVERRIDE;
   virtual bool DeallocPTCPServerSocketChild(PTCPServerSocketChild*) MOZ_OVERRIDE;
   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,
                               const URIParams&,
                               const OptionalURIParams&) MOZ_OVERRIDE;
   virtual bool DeallocPRemoteOpenFileChild(PRemoteOpenFileChild*) MOZ_OVERRIDE;
   virtual PRtspControllerChild* AllocPRtspControllerChild() MOZ_OVERRIDE;
   virtual bool DeallocPRtspControllerChild(PRtspControllerChild*) MOZ_OVERRIDE;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -474,29 +474,32 @@ NeckoParent::DeallocPUDPSocketParent(PUD
 {
   UDPSocketParent* p = static_cast<UDPSocketParent*>(actor);
   p->Release();
   return true;
 }
 
 PDNSRequestParent*
 NeckoParent::AllocPDNSRequestParent(const nsCString& aHost,
-                                    const uint32_t& aFlags)
+                                    const uint32_t& aFlags,
+                                    const nsCString& aNetworkInterface)
 {
   DNSRequestParent *p = new DNSRequestParent();
   p->AddRef();
   return p;
 }
 
 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;
 }
 
 bool
 NeckoParent::DeallocPDNSRequestParent(PDNSRequestParent* aParent)
 {
   DNSRequestParent *p = static_cast<DNSRequestParent*>(aParent);
   p->Release();
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -148,20 +148,22 @@ protected:
                                                const uint16_t& aLocalPort,
                                                const uint16_t& aBacklog,
                                                const nsString& aBinaryType) MOZ_OVERRIDE;
   virtual bool DeallocPTCPServerSocketParent(PTCPServerSocketParent*) MOZ_OVERRIDE;
   virtual PUDPSocketParent* AllocPUDPSocketParent(const nsCString& aFilter) MOZ_OVERRIDE;
   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;
   virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
                                          const uint16_t& flags,
                                          const nsresult& reason) MOZ_OVERRIDE;
 
   virtual mozilla::ipc::IProtocol*
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -62,17 +62,17 @@ parent:
   PWyciwygChannel();
   PFTPChannel(PBrowserOrId browser, SerializedLoadContext loadContext,
               FTPChannelCreationArgs args);
 
   PWebSocket(PBrowserOrId browser, SerializedLoadContext loadContext);
   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,
                   OptionalURIParams appuri);
 
   HTMLDNSPrefetch(nsString hostname, uint16_t flags);
   CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason);
   PRtspController();
new file mode 100644
--- /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();
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -168,16 +168,17 @@ skip-if = bits != 32
 [test_compareURIs.js]
 [test_compressappend.js]
 [test_content_encoding_gzip.js]
 [test_content_sniffer.js]
 [test_cookie_header.js]
 [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]
 [test_dns_proxy_bypass.js]
 [test_duplicate_headers.js]
 [test_chunked_responses.js]
 [test_content_length_underrun.js]
 [test_event_sink.js]
new file mode 100644
--- /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");
+}
--- a/netwerk/test/unit_ipc/xpcshell.ini
+++ b/netwerk/test/unit_ipc/xpcshell.ini
@@ -7,16 +7,17 @@ support-files = child_app_offline.js
 [test_bug528292_wrap.js]
 [test_bug248970_cookie_wrap.js]
 [test_cacheflags_wrap.js]
 [test_cache_jar_wrap.js]
 [test_channel_close_wrap.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]
 [test_head_wrap.js]
 [test_headers_wrap.js]
 [test_httpsuspend_wrap.js]
 [test_post_wrap.js]
 [test_progress_wrap.js]