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]