michael@0: /* vim:set ts=4 sw=4 sts=4 et cin: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsHostResolver_h__ michael@0: #define nsHostResolver_h__ michael@0: michael@0: #include "nscore.h" michael@0: #include "prclist.h" michael@0: #include "prnetdb.h" michael@0: #include "pldhash.h" michael@0: #include "mozilla/CondVar.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "nsISupportsImpl.h" michael@0: #include "nsIDNSListener.h" michael@0: #include "nsString.h" michael@0: #include "nsTArray.h" michael@0: #include "mozilla/net/DNS.h" michael@0: #include "mozilla/net/DashboardTypes.h" michael@0: #include "mozilla/TimeStamp.h" michael@0: michael@0: class nsHostResolver; michael@0: class nsHostRecord; michael@0: class nsResolveHostCallback; michael@0: michael@0: #define MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY 3 michael@0: #define MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY 5 michael@0: #define MAX_NON_PRIORITY_REQUESTS 150 michael@0: michael@0: #define MAX_RESOLVER_THREADS (MAX_RESOLVER_THREADS_FOR_ANY_PRIORITY + \ michael@0: MAX_RESOLVER_THREADS_FOR_HIGH_PRIORITY) michael@0: michael@0: struct nsHostKey michael@0: { michael@0: const char *host; michael@0: uint16_t flags; michael@0: uint16_t af; michael@0: }; michael@0: michael@0: /** michael@0: * nsHostRecord - ref counted object type stored in host resolver cache. michael@0: */ michael@0: class nsHostRecord : public PRCList, public nsHostKey michael@0: { michael@0: typedef mozilla::Mutex Mutex; michael@0: michael@0: public: michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHostRecord) michael@0: michael@0: /* instantiates a new host record */ michael@0: static nsresult Create(const nsHostKey *key, nsHostRecord **record); michael@0: michael@0: /* a fully resolved host record has either a non-null |addr_info| or |addr| michael@0: * field. if |addr_info| is null, it implies that the |host| is an IP michael@0: * address literal. in which case, |addr| contains the parsed address. michael@0: * otherwise, if |addr_info| is non-null, then it contains one or many michael@0: * IP addresses corresponding to the given host name. if both |addr_info| michael@0: * and |addr| are null, then the given host has not yet been fully resolved. michael@0: * |af| is the address family of the record we are querying for. michael@0: */ michael@0: michael@0: /* the lock protects |addr_info| and |addr_info_gencnt| because they michael@0: * are mutable and accessed by the resolver worker thread and the michael@0: * nsDNSService2 class. |addr| doesn't change after it has been michael@0: * assigned a value. only the resolver worker thread modifies michael@0: * nsHostRecord (and only in nsHostResolver::OnLookupComplete); michael@0: * the other threads just read it. therefore the resolver worker michael@0: * thread doesn't need to lock when reading |addr_info|. michael@0: */ michael@0: Mutex addr_info_lock; michael@0: int addr_info_gencnt; /* generation count of |addr_info| */ michael@0: mozilla::net::AddrInfo *addr_info; michael@0: mozilla::net::NetAddr *addr; michael@0: bool negative; /* True if this record is a cache of a failed lookup. michael@0: Negative cache entries are valid just like any other michael@0: (though never for more than 60 seconds), but a use michael@0: of that negative entry forces an asynchronous refresh. */ michael@0: michael@0: mozilla::TimeStamp expiration; michael@0: michael@0: bool HasUsableResult(uint16_t queryFlags) const; michael@0: michael@0: // hold addr_info_lock when calling the blacklist functions michael@0: bool Blacklisted(mozilla::net::NetAddr *query); michael@0: void ResetBlacklist(); michael@0: void ReportUnusable(mozilla::net::NetAddr *addr); michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; michael@0: michael@0: private: michael@0: friend class nsHostResolver; michael@0: michael@0: PRCList callbacks; /* list of callbacks */ michael@0: michael@0: bool resolving; /* true if this record is being resolved, which means michael@0: * that it is either on the pending queue or owned by michael@0: * one of the worker threads. */ michael@0: michael@0: bool onQueue; /* true if pending and on the queue (not yet given to getaddrinfo())*/ michael@0: bool usingAnyThread; /* true if off queue and contributing to mActiveAnyThreadCount */ michael@0: bool mDoomed; /* explicitly expired */ michael@0: michael@0: // a list of addresses associated with this record that have been reported michael@0: // as unusable. the list is kept as a set of strings to make it independent michael@0: // of gencnt. michael@0: nsTArray mBlacklistedItems; michael@0: michael@0: nsHostRecord(const nsHostKey *key); /* use Create() instead */ michael@0: ~nsHostRecord(); michael@0: }; michael@0: michael@0: /** michael@0: * ResolveHost callback object. It's PRCList members are used by michael@0: * the nsHostResolver and should not be used by anything else. michael@0: */ michael@0: class NS_NO_VTABLE nsResolveHostCallback : public PRCList michael@0: { michael@0: public: michael@0: /** michael@0: * OnLookupComplete michael@0: * michael@0: * this function is called to complete a host lookup initiated by michael@0: * nsHostResolver::ResolveHost. it may be invoked recursively from michael@0: * ResolveHost or on an unspecified background thread. michael@0: * michael@0: * NOTE: it is the responsibility of the implementor of this method michael@0: * to handle the callback in a thread safe manner. michael@0: * michael@0: * @param resolver michael@0: * nsHostResolver object associated with this result michael@0: * @param record michael@0: * the host record containing the results of the lookup michael@0: * @param status michael@0: * if successful, |record| contains non-null results michael@0: */ michael@0: virtual void OnLookupComplete(nsHostResolver *resolver, michael@0: nsHostRecord *record, michael@0: nsresult status) = 0; michael@0: /** michael@0: * EqualsAsyncListener michael@0: * michael@0: * Determines if the listener argument matches the listener member var. michael@0: * For subclasses not implementing a member listener, should return false. michael@0: * For subclasses having a member listener, the function should check if michael@0: * they are the same. Used for cases where a pointer to an object michael@0: * implementing nsResolveHostCallback is unknown, but a pointer to michael@0: * the original listener is known. michael@0: * michael@0: * @param aListener michael@0: * nsIDNSListener object associated with the original request michael@0: */ michael@0: virtual bool EqualsAsyncListener(nsIDNSListener *aListener) = 0; michael@0: michael@0: virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf) const = 0; michael@0: }; michael@0: michael@0: /** michael@0: * nsHostResolver - an asynchronous host name resolver. michael@0: */ michael@0: class nsHostResolver michael@0: { michael@0: typedef mozilla::CondVar CondVar; michael@0: typedef mozilla::Mutex Mutex; michael@0: michael@0: public: michael@0: /** michael@0: * host resolver instances are reference counted. michael@0: */ michael@0: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(nsHostResolver) michael@0: michael@0: /** michael@0: * creates an addref'd instance of a nsHostResolver object. michael@0: */ michael@0: static nsresult Create(uint32_t maxCacheEntries, // zero disables cache michael@0: uint32_t maxCacheLifetime, // seconds michael@0: uint32_t lifetimeGracePeriod, // seconds michael@0: nsHostResolver **resolver); michael@0: michael@0: /** michael@0: * puts the resolver in the shutdown state, which will cause any pending michael@0: * callbacks to be detached. any future calls to ResolveHost will fail. michael@0: */ michael@0: void Shutdown(); michael@0: michael@0: /** michael@0: * resolve the given hostname asynchronously. the caller can synthesize michael@0: * a synchronous host lookup using a lock and a cvar. as noted above michael@0: * the callback will occur re-entrantly from an unspecified thread. the michael@0: * host lookup cannot be canceled (cancelation can be layered above this michael@0: * by having the callback implementation return without doing anything). michael@0: */ michael@0: nsresult ResolveHost(const char *hostname, michael@0: uint16_t flags, michael@0: uint16_t af, michael@0: nsResolveHostCallback *callback); michael@0: michael@0: /** michael@0: * removes the specified callback from the nsHostRecord for the given michael@0: * hostname, flags, and address family. these parameters should correspond michael@0: * to the parameters passed to ResolveHost. this function executes the michael@0: * callback if the callback is still pending with the given status. michael@0: */ michael@0: void DetachCallback(const char *hostname, michael@0: uint16_t flags, michael@0: uint16_t af, michael@0: nsResolveHostCallback *callback, michael@0: nsresult status); michael@0: michael@0: /** michael@0: * Cancels an async request associated with the hostname, flags, michael@0: * address family and listener. Cancels first callback found which matches michael@0: * these criteria. These parameters should correspond to the parameters michael@0: * passed to ResolveHost. If this is the last callback associated with the michael@0: * host record, it is removed from any request queues it might be on. michael@0: */ michael@0: void CancelAsyncRequest(const char *host, michael@0: uint16_t flags, michael@0: uint16_t af, michael@0: nsIDNSListener *aListener, michael@0: nsresult status); michael@0: /** michael@0: * values for the flags parameter passed to ResolveHost and DetachCallback michael@0: * that may be bitwise OR'd together. michael@0: * michael@0: * NOTE: in this implementation, these flags correspond exactly in value michael@0: * to the flags defined on nsIDNSService. michael@0: */ michael@0: enum { michael@0: RES_BYPASS_CACHE = 1 << 0, michael@0: RES_CANON_NAME = 1 << 1, michael@0: RES_PRIORITY_MEDIUM = 1 << 2, michael@0: RES_PRIORITY_LOW = 1 << 3, michael@0: RES_SPECULATE = 1 << 4, michael@0: //RES_DISABLE_IPV6 = 1 << 5, // Not used michael@0: RES_OFFLINE = 1 << 6 michael@0: }; michael@0: michael@0: size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; michael@0: michael@0: private: michael@0: nsHostResolver(uint32_t maxCacheEntries = 50, uint32_t maxCacheLifetime = 60, michael@0: uint32_t lifetimeGracePeriod = 0); michael@0: ~nsHostResolver(); michael@0: michael@0: nsresult Init(); michael@0: nsresult IssueLookup(nsHostRecord *); michael@0: bool GetHostToLookup(nsHostRecord **m); michael@0: void OnLookupComplete(nsHostRecord *, nsresult, mozilla::net::AddrInfo *); michael@0: void DeQueue(PRCList &aQ, nsHostRecord **aResult); michael@0: void ClearPendingQueue(PRCList *aPendingQueue); michael@0: nsresult ConditionallyCreateThread(nsHostRecord *rec); michael@0: michael@0: /** michael@0: * Starts a new lookup in the background for entries that are in the grace michael@0: * period with a failed connect or all cached entries are negative. michael@0: */ michael@0: nsresult ConditionallyRefreshRecord(nsHostRecord *rec, const char *host); michael@0: michael@0: static void MoveQueue(nsHostRecord *aRec, PRCList &aDestQ); michael@0: michael@0: static void ThreadFunc(void *); michael@0: michael@0: enum { michael@0: METHOD_HIT = 1, michael@0: METHOD_RENEWAL = 2, michael@0: METHOD_NEGATIVE_HIT = 3, michael@0: METHOD_LITERAL = 4, michael@0: METHOD_OVERFLOW = 5, michael@0: METHOD_NETWORK_FIRST = 6, michael@0: METHOD_NETWORK_SHARED = 7 michael@0: }; michael@0: michael@0: uint32_t mMaxCacheEntries; michael@0: mozilla::TimeDuration mMaxCacheLifetime; // granularity seconds michael@0: mozilla::TimeDuration mGracePeriod; // granularity seconds michael@0: mutable Mutex mLock; // mutable so SizeOfIncludingThis can be const michael@0: CondVar mIdleThreadCV; michael@0: uint32_t mNumIdleThreads; michael@0: uint32_t mThreadCount; michael@0: uint32_t mActiveAnyThreadCount; michael@0: PLDHashTable mDB; michael@0: PRCList mHighQ; michael@0: PRCList mMediumQ; michael@0: PRCList mLowQ; michael@0: PRCList mEvictionQ; michael@0: uint32_t mEvictionQSize; michael@0: uint32_t mPendingCount; michael@0: PRTime mCreationTime; michael@0: bool mShutdown; michael@0: PRIntervalTime mLongIdleTimeout; michael@0: PRIntervalTime mShortIdleTimeout; michael@0: michael@0: public: michael@0: /* michael@0: * Called by the networking dashboard via the DnsService2 michael@0: */ michael@0: void GetDNSCacheEntries(nsTArray *); michael@0: }; michael@0: michael@0: #endif // nsHostResolver_h__