author | Dragana Damjanovic <dd.mozilla@gmail.com> |
Thu, 19 Feb 2015 00:14:00 -0500 | |
changeset 229863 | 0c26eb16bc31907d47e5312aacbcb5e301637ec0 |
parent 229862 | 05666fcd8e286a199f597032caa1b39246f618c8 |
child 229864 | 09f68bee144887ab1e2d57683cb4ea83346082f4 |
push id | 55829 |
push user | ryanvm@gmail.com |
push date | Thu, 19 Feb 2015 15:35:48 +0000 |
treeherder | mozilla-inbound@0819ea44a310 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mayhemer |
bugs | 1108957 |
milestone | 38.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- 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,21 @@ #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" + +#if defined(ANDROID) && ANDROID_VERSION > 19 +#include <resolv_netid.h> +#endif #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 +215,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 +309,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 +369,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/moz.build +++ b/netwerk/dns/moz.build @@ -62,8 +62,11 @@ GENERATED_FILES = [ etld_data = GENERATED_FILES['etld_data.inc'] etld_data.script = 'prepare_tlds.py' etld_data.inputs = ['effective_tld_names.dat'] # need to include etld_data.inc LOCAL_INCLUDES += [ '/netwerk/base', ] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['ANDROID_VERSION'] > '19': + CXXFLAGS += ['-I%s/bionic/libc/dns/include' % CONFIG['ANDROID_SOURCE']]
--- 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; } @@ -226,62 +234,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; @@ -386,30 +399,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 = @@ -421,29 +436,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 { @@ -723,22 +742,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. @@ -758,40 +779,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) { @@ -813,49 +837,53 @@ 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"); TimeStamp now = TimeStamp::NowLoRes(); if (unspecHe && unspecHe->rec->HasUsableResult(now, 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->CopyExpirationTimesAndFlagsFrom(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 @@ -888,48 +916,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. @@ -957,24 +989,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) { @@ -1015,18 +1048,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"); @@ -1180,17 +1215,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; @@ -1200,18 +1236,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 @@ -1272,17 +1308,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 } @@ -1305,24 +1342,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) { @@ -1386,36 +1424,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; @@ -1430,22 +1470,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")); } @@ -1484,16 +1526,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 notifications for the requests. + // 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]