/*++ Copyright (c) 1994 Microsoft Corporation Module Name: rescache.cxx Abstract: Contains functions which manipulate resolver cache for winsock name resolution calls Contents: InitializeResolverCache TerminateResolverCache QueryResolverCache AddResolverCacheEntry FlushResolverCache ReleaseResolverCacheEntry (RemoveCacheEntry) (ResolverCacheHit) (AddrInfoMatch) (CreateCacheEntry) Author: Richard L Firth (rfirth) 10-Jul-1994 Environment: Win-16/32 user level Revision History: rfirth 10-Jul-1994 Created --*/ // // includes // #include "wininetp.h" // // private manifests // // // private macros // #define SET_EXPIRATION_TIME(cacheEntry) // // private data // PRIVATE BOOL ResolverCacheInitialized = FALSE; // // DnsCachingEnabled - caching is enabled by default // PRIVATE BOOL DnsCachingEnabled = TRUE; // // DnsCacheTimeout - number of seconds before a cache entry expires. This value // is added to the current time (in seconds) to get the expiry time // PRIVATE DWORD DnsCacheTimeout = DEFAULT_DNS_CACHE_TIMEOUT; // // MaximumDnsCacheEntries - the maximum number of RESOLVER_CACHE_ENTRYs in the // cache before we start throwing out the LRU // PRIVATE INT MaximumDnsCacheEntries = DEFAULT_DNS_CACHE_ENTRIES; // // CurrentDnsCacheEntries - the number of RESOLVER_CACHE_ENTRYs currently in the // cache // PRIVATE INT CurrentDnsCacheEntries = 0; // // ResolverCache - serialized list of RESOLVER_CACHE_ENTRYs, kept in MRU order. // We only need to remove the tail of the list to remove the LRU entry // PRIVATE SERIALIZED_LIST ResolverCache = {0}; // // private prototypes // PRIVATE VOID RemoveCacheEntry( IN LPRESOLVER_CACHE_ENTRY lpCacheEntry ); PRIVATE BOOL ResolverCacheHit( IN LPRESOLVER_CACHE_ENTRY lpCacheEntry, IN LPSTR Name OPTIONAL, IN LPSOCKADDR Address OPTIONAL ); PRIVATE BOOL AddrInfoMatch( IN LPADDRINFO AddrInfo, IN LPSTR Name OPTIONAL, IN LPSOCKADDR Address OPTIONAL ); PRIVATE LPRESOLVER_CACHE_ENTRY CreateCacheEntry( IN LPSTR lpszHostName, IN LPADDRINFO AddrInfo, IN DWORD TimeToLive ); #if INET_DEBUG PRIVATE DEBUG_FUNCTION LPSTR CacheTimestr( IN DWORD Time ); PRIVATE DEBUG_FUNCTION LPSTR CacheAddrInfoStr( IN LPADDRINFO AddrInfo ); PRIVATE DEBUG_FUNCTION LPSTR CacheMapSockAddress( IN LPSOCKADDR Address ); #endif // // functions // VOID InitializeResolverCache( VOID ) /*++ Routine Description: Initializes the resolver cache: * Initializes the cache list anchor * loads the cache Arguments: None. Return Value: None. --*/ { DEBUG_ENTER((DBG_SOCKETS, None, "InitializeResolverCache", NULL )); if (!(BOOL)InterlockedExchange((LPLONG)&ResolverCacheInitialized, TRUE)) { InternetReadRegistryDword("DnsCacheEnabled", (LPDWORD)&DnsCachingEnabled); InternetReadRegistryDword("DnsCacheEntries", (LPDWORD)&MaximumDnsCacheEntries); InternetReadRegistryDword("DnsCacheTimeout", &DnsCacheTimeout); InitializeSerializedList(&ResolverCache); // // if the size of the cache in the registry is 0 then its the same as // no caching // if (MaximumDnsCacheEntries == 0) { DnsCachingEnabled = FALSE; } } else { // // shouldn't be calling this more than once // INET_ASSERT(FALSE); } DEBUG_LEAVE(0); } VOID TerminateResolverCache( VOID ) /*++ Routine Description: Free up all resources allocated by InitializeResolverCache() Arguments: None. Return Value: None. --*/ { DEBUG_ENTER((DBG_SOCKETS, None, "TerminateResolverCache", NULL )); if (InterlockedExchange((LPLONG)&ResolverCacheInitialized, FALSE)) { // // short-circuit any other cache attempts (shouldn't be any by now) // DnsCachingEnabled = FALSE; // // and clear out the list // FlushResolverCache(); // // we are done with the serialized list // TerminateSerializedList(&ResolverCache); } DEBUG_LEAVE(0); } LPRESOLVER_CACHE_ENTRY QueryResolverCache( IN LPSTR Name OPTIONAL, IN LPSOCKADDR Address OPTIONAL, OUT LPADDRINFO * AddrInfo, OUT LPDWORD TimeToLive ) /*++ Routine Description: Checks if Name is stored in the last resolved name cache. If the entry is found, but has expired then it is removed from the cache Arguments: Name - pointer to name string Address - pointer to IP address (in sockaddr format) AddrInfo - pointer to returned pointer to addrinfo TimeToLive - pointer to returned time to live Return Value: BOOL --*/ { DEBUG_ENTER((DBG_SOCKETS, Bool, "QueryResolverCache", "%q, %s, %#x, %#x", Name, CacheMapSockAddress(Address), AddrInfo, TimeToLive )); LPRESOLVER_CACHE_ENTRY lpEntry; if (!DnsCachingEnabled) { DEBUG_PRINT(SOCKETS, WARNING, ("DNS caching disabled\n" )); *AddrInfo = NULL; lpEntry = NULL; goto quit; } LockSerializedList(&ResolverCache); LPRESOLVER_CACHE_ENTRY cacheEntry; LPRESOLVER_CACHE_ENTRY previousEntry; DWORD timeNow; cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(&ResolverCache); timeNow = (DWORD)time(NULL); while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache)) { // // on every cache lookup, purge any stale entries. LIVE_FOREVER means // that we don't expect the entry's net address to expire, but it // DOESN'T mean that we can't throw out the entry if its the LRU and // we're at maximum cache capacity. We can't do this if the item is // still in-use. In this case, we mark it stale // if ((cacheEntry->ExpirationTime != LIVE_FOREVER) && (cacheEntry->ExpirationTime <= timeNow)) { // // if reference count not zero then another thread is using // this entry - mark as stale else delete it // if (cacheEntry->ReferenceCount != 0) { INET_ASSERT(cacheEntry->State == ENTRY_IN_USE); cacheEntry->State = ENTRY_DELETE; } else { // // this entry is stale; throw it out // "my hovercraft is full of eels" // DEBUG_PRINT(SOCKETS, INFO, ("throwing out stale DNS entry %q, expiry = %s\n", cacheEntry->AddrInfo->ai_canonname, CacheTimestr(cacheEntry->ExpirationTime) )); // // BUGBUG - what happens if ExpirationTime == timeNow? // previousEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Blink; RemoveCacheEntry(cacheEntry); cacheEntry = previousEntry; } } else if (ResolverCacheHit(cacheEntry, Name, Address) && ((cacheEntry->State == ENTRY_UNUSED) || (cacheEntry->State == ENTRY_IN_USE))) { // // we found the entry, and it still has time to live. Make it the // head of the list (MRU first), set the state to in-use and increase // the reference count // RemoveFromSerializedList(&ResolverCache, &cacheEntry->ListEntry); InsertAtHeadOfSerializedList(&ResolverCache, &cacheEntry->ListEntry); cacheEntry->State = ENTRY_IN_USE; ++cacheEntry->ReferenceCount; *AddrInfo = cacheEntry->AddrInfo; lpEntry = cacheEntry; DEBUG_PRINT(SOCKETS, INFO, ("entry found in DNS cache\n" )); goto done; } cacheEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Flink; } *AddrInfo = NULL; lpEntry = NULL; DEBUG_PRINT(SOCKETS, INFO, ("didn't find entry in DNS cache\n" )); done: UnlockSerializedList(&ResolverCache); quit: DEBUG_LEAVE(lpEntry); return lpEntry; } VOID AddResolverCacheEntry( IN LPSTR lpszHostName, IN LPADDRINFO AddrInfo, IN DWORD TimeToLive ) /*++ Routine Description: Adds an addrinfo pointer to the cache. Creates a new entry to hold it and links it into the cache list, displacing the LRU entry if required. If we cannot create the entry, the addrinfo is freed, no errors returned N.B.: Calling this routine gives the resolver cache "ownership" of the addrinfo chain. Caller should not use AddrInfo pointer afterwards. Arguments: lpszHostName - the name we originally requested be resolved. May be different than the names returned by the resolver, e.g. "proxy" => "proxy1.microsoft.com, proxy2.microsoft.com" AddrInfo - pointer to addrinfo chain to add to the cache TimeToLive - amount of time this information has to live. Can be: LIVE_FOREVER - don't timeout (but can be discarded) LIVE_DEFAULT - use the default value anything else - number of seconds to live Return Value: None. --*/ { DEBUG_ENTER((DBG_SOCKETS, None, "AddResolverCacheEntry", "%q, %#x, %d", lpszHostName, AddrInfo, TimeToLive )); BOOL bAdded = FALSE; if (!DnsCachingEnabled) { DEBUG_PRINT(SOCKETS, WARNING, ("DNS caching disabled\n" )); goto quit; } LockSerializedList(&ResolverCache); // // check that the entry is not already in the cache - 2 or more threads may // have been simultaneously resolving the same name // LPADDRINFO lpAddrInfo; DWORD ttl; LPRESOLVER_CACHE_ENTRY lpResolverCacheEntry; INET_ASSERT(lpszHostName != NULL); if (!(lpResolverCacheEntry = QueryResolverCache(lpszHostName, NULL, &lpAddrInfo, &ttl))) { LPRESOLVER_CACHE_ENTRY cacheEntry; // // remove as many entries as we can beginning at the tail of the list. // We try to remove enough to get the cache size back below the limit. // This may consist of removing expired entries or entries marked as // DELETE. If there are expired, in-use entries then we mark them as // DELETE. This may result in the cache list growing until those threads // which have referenced cache entries release them // cacheEntry = (LPRESOLVER_CACHE_ENTRY)TailOfSerializedList(&ResolverCache); while ((CurrentDnsCacheEntries >= MaximumDnsCacheEntries) && (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache))) { // // cache has maximum entries: throw out the Least Recently Used (its // the one at the back of the queue, ma'am) but only if no-one else // is currently accessing it // if ((cacheEntry->State != ENTRY_IN_USE) && (cacheEntry->ReferenceCount == 0)) { INET_ASSERT((cacheEntry->State == ENTRY_UNUSED) || (cacheEntry->State == ENTRY_DELETE)); DEBUG_PRINT(SOCKETS, INFO, ("throwing out LRU %q\n", cacheEntry->AddrInfo->ai_canonname )); LPRESOLVER_CACHE_ENTRY nextEntry; nextEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Flink; RemoveCacheEntry(cacheEntry); cacheEntry = nextEntry; } else if (cacheEntry->State == ENTRY_IN_USE) { // // this entry needs to be freed when it is released // cacheEntry->State = ENTRY_DELETE; } cacheEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Blink; } // // add the entry at the head of the queue - it is the Most Recently Used // after all. If we fail to allocate memory, its no problem: it'll just // take a little longer if this entry would have been hit before we needed // to throw out another entry // if (cacheEntry = CreateCacheEntry(lpszHostName, AddrInfo, TimeToLive)) { DEBUG_PRINT(SOCKETS, INFO, ("caching %q, expiry = %s\n", CacheAddrInfoStr(cacheEntry->AddrInfo), CacheTimestr(cacheEntry->ExpirationTime) )); InsertAtHeadOfSerializedList(&ResolverCache, &cacheEntry->ListEntry); ++CurrentDnsCacheEntries; bAdded = TRUE; } } else { // // this entry is already in the cache. 2 or more threads must have been // resolving the same name simultaneously. We just bump the expiration // time to the more recent value // DEBUG_PRINT(SOCKETS, WARNING, ("found %q already in the cache!?\n", lpszHostName )); ReleaseResolverCacheEntry(lpResolverCacheEntry); } UnlockSerializedList(&ResolverCache); quit: if (!bAdded) { // // failed to add this entry to the cache, so free it // _I_freeaddrinfo(AddrInfo); } DEBUG_LEAVE(0); } VOID FlushResolverCache( VOID ) /*++ Routine Description: Removes all entries in DNS resolver cache Arguments: None. Return Value: None. --*/ { DEBUG_ENTER((DBG_SOCKETS, None, "FlushResolverCache", NULL )); LPRESOLVER_CACHE_ENTRY cacheEntry; LPRESOLVER_CACHE_ENTRY previousEntry; LockSerializedList(&ResolverCache); previousEntry = (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache); cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(&ResolverCache); while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache)) { if (cacheEntry->State == ENTRY_UNUSED) { RemoveCacheEntry(cacheEntry); } else { DEBUG_PRINT(SOCKETS, WARNING, ("cache entry %#x (%q) still in-use\n", cacheEntry->HostName )); cacheEntry->State = ENTRY_DELETE; previousEntry = cacheEntry; } cacheEntry = (LPRESOLVER_CACHE_ENTRY)previousEntry->ListEntry.Flink; } UnlockSerializedList(&ResolverCache); DEBUG_LEAVE(0); } VOID ReleaseResolverCacheEntry( IN LPRESOLVER_CACHE_ENTRY cacheEntry ) /*++ Routine Description: Either mark a entry unused or if it is stale, delete it Arguments: lpAddrInfo - pointer to AddrInfo field of entry to free Return Value: None. --*/ { DEBUG_ENTER((DBG_SOCKETS, None, "ReleaseResolverCacheEntry", "%#x, %#x", cacheEntry, (cacheEntry ? cacheEntry->AddrInfo : NULL) )); LockSerializedList(&ResolverCache); // // reference count should never go below zero! // INET_ASSERT(cacheEntry->ReferenceCount > 0); if (--cacheEntry->ReferenceCount <= 0) { // // last releaser gets to decide what to do - mark unused or delete // if (cacheEntry->State == ENTRY_IN_USE) { cacheEntry->State = ENTRY_UNUSED; } else if (cacheEntry->State == ENTRY_DELETE) { // // entry is already stale - throw it out // RemoveCacheEntry(cacheEntry); } else { // // unused? or bogus value? Someone changed state while refcount // not zero? // INET_ASSERT((cacheEntry->State == ENTRY_IN_USE) || (cacheEntry->State == ENTRY_DELETE)); } } UnlockSerializedList(&ResolverCache); DEBUG_LEAVE(0); } VOID ThrowOutResolverCacheEntry( IN LPADDRINFO lpAddrInfo ) /*++ Routine Description: Removes this entry from the DNS cache, based on the host name. We assume that the entry came from the cache, so unless it has been already purged, we should be able to throw it out Arguments: lpAddrInfo - pointer to addrinfo field with name of entry to throw out Return Value: None. --*/ { DEBUG_ENTER((DBG_SOCKETS, None, "ThrowOutResolverCacheEntry", "%#x [%q]", lpAddrInfo, lpAddrInfo->ai_canonname )); if (DnsCachingEnabled) { LockSerializedList(&ResolverCache); LPRESOLVER_CACHE_ENTRY cacheEntry; cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(&ResolverCache); while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(&ResolverCache)) { if (AddrInfoMatch(cacheEntry->AddrInfo, lpAddrInfo->ai_canonname, NULL)) { // // if the entry is unused then we can delete it, else we have // to leave it to the thread with the last reference // if (cacheEntry->State == ENTRY_UNUSED) { RemoveCacheEntry(cacheEntry); } else { cacheEntry->State = ENTRY_DELETE; } break; } } UnlockSerializedList(&ResolverCache); } else { DEBUG_PRINT(SOCKETS, WARNING, ("DNS caching disabled\n" )); } DEBUG_LEAVE(0); } PRIVATE VOID RemoveCacheEntry( IN LPRESOLVER_CACHE_ENTRY lpCacheEntry ) /*++ Routine Description: Takes a cache entry off the list and frees it N.B.: This function must be called with the resolver cache serialized list already locked Arguments: lpCacheEntry - currently queued entry to remove Return Value: None. --*/ { DEBUG_ENTER((DBG_SOCKETS, None, "RemoveCacheEntry", "%#x", lpCacheEntry )); RemoveFromSerializedList(&ResolverCache, &lpCacheEntry->ListEntry); INET_ASSERT(lpCacheEntry->ReferenceCount == 0); INET_ASSERT((lpCacheEntry->State == ENTRY_UNUSED) || (lpCacheEntry->State == ENTRY_DELETE)); DEBUG_PRINT(SOCKETS, INFO, ("throwing out %q, expiry = %s\n", CacheAddrInfoStr(lpCacheEntry->AddrInfo), CacheTimestr(lpCacheEntry->ExpirationTime) )); _I_freeaddrinfo(lpCacheEntry->AddrInfo); lpCacheEntry = (LPRESOLVER_CACHE_ENTRY)FREE_MEMORY((HLOCAL)lpCacheEntry); INET_ASSERT(lpCacheEntry == NULL); --CurrentDnsCacheEntries; DEBUG_PRINT(SOCKETS, INFO, ("CurrentDnsCacheEntries = %d\n", CurrentDnsCacheEntries )); INET_ASSERT((CurrentDnsCacheEntries >= 0) && (CurrentDnsCacheEntries <= MaximumDnsCacheEntries)); DEBUG_LEAVE(0); } PRIVATE BOOL ResolverCacheHit( IN LPRESOLVER_CACHE_ENTRY lpCacheEntry, IN LPSTR Name OPTIONAL, IN LPSOCKADDR Address OPTIONAL ) /*++ Routine Description: Checks this RESOLVER_CACHE_ENTRY for a match with Name or Address. If Name, can match with ai_canonname in addrinfo, or with originally resolved name Arguments: lpCacheEntry - pointer to RESOLVER_CACHE_ENTRY to check Name - optional name to check Address - optional server address to check Return Value: BOOL --*/ { DEBUG_ENTER((DBG_SOCKETS, Bool, "ResolverCacheHit", "%#x, %q, %s", lpCacheEntry, Name, CacheMapSockAddress(Address) )); BOOL found; if ((Name != NULL) && (lpCacheEntry->HostName != NULL) && (lstrcmpi(lpCacheEntry->HostName, Name) == 0)) { DEBUG_PRINT(SOCKETS, INFO, ("matched name %q\n", lpCacheEntry->HostName )); found = TRUE; } else { found = FALSE; Name = NULL; } if (!found && Address) { found = AddrInfoMatch(lpCacheEntry->AddrInfo, Name, Address); } DEBUG_LEAVE(found); return found; } PRIVATE BOOL AddrInfoMatch( IN LPADDRINFO AddrInfo, IN LPSTR Name OPTIONAL, IN LPSOCKADDR Address OPTIONAL ) /*++ Routine Description: Compares a getaddrinfo result for a match with a host name or address Arguments: AddrInfo - pointer to addrinfo chain to compare Name - pointer to name string Address - pointer to IP address (as a sockaddr) Return Value: BOOL --*/ { DEBUG_ENTER((DBG_SOCKETS, Bool, "AddrInfoMatch", "%#x, %q, %s", AddrInfo, Name, CacheMapSockAddress(Address) )); BOOL found = FALSE; if (Name) { if ((AddrInfo->ai_canonname != NULL) && (lstrcmpi(AddrInfo->ai_canonname, Name) == 0)) found = TRUE; } else { INET_ASSERT(Address != NULL); do { if ((AddrInfo->ai_addr->sa_family == Address->sa_family) && (memcmp(AddrInfo->ai_addr, Address, AddrInfo->ai_addrlen) == 0)) { DEBUG_PRINT(SOCKETS, INFO, ("matched %s\n", CacheMapSockAddress(Address) )); found = TRUE; break; } } while (AddrInfo = AddrInfo->ai_next); } if (found) { DEBUG_PRINT(SOCKETS, INFO, ("addrinfo = %q\n", CacheAddrInfoStr(AddrInfo) )); } DEBUG_LEAVE(found); return found; } PRIVATE LPRESOLVER_CACHE_ENTRY CreateCacheEntry( IN LPSTR lpszHostName, IN LPADDRINFO AddrInfo, IN DWORD TimeToLive ) /*++ Routine Description: Allocates a RESOLVER_CACHE_ENTRY and packs it with the addrinfo information and sets the ExpirationTime Arguments: lpszHostName - name we resolved AddrInfo - pointer to addrinfo chain to store in new entry TimeToLive - amount of time before this entry expires Return Value: LPRESOLVER_CACHE_ENTRY --*/ { LPRESOLVER_CACHE_ENTRY cacheEntry; INET_ASSERT(lpszHostName != NULL); // // only copy lpszHostName if it is different from the name in addrinfo // UINT hostNameSize; if ((AddrInfo->ai_canonname != NULL) && lstrcmpi(AddrInfo->ai_canonname, lpszHostName) != 0) { hostNameSize = lstrlen(lpszHostName) + 1; } else if (AddrInfo->ai_canonname == NULL) { // if ap_canonname is null, we have to save the lpszHoatName hostNameSize = lstrlen(lpszHostName) + 1; } else { hostNameSize = 0; } // // allocate space for the cache entry // cacheEntry = (LPRESOLVER_CACHE_ENTRY)ALLOCATE_MEMORY(LMEM_FIXED, sizeof(RESOLVER_CACHE_ENTRY) + hostNameSize ); if (cacheEntry != NULL) { // // cache the getaddrinfo result // cacheEntry->AddrInfo = AddrInfo; // // copy the host name to the end of the buffer if required // if (hostNameSize != 0) { cacheEntry->HostName = (LPSTR)(cacheEntry + 1); RtlCopyMemory(cacheEntry->HostName, lpszHostName, hostNameSize); } else { cacheEntry->HostName = NULL; } // // calculate the expiration time as the current time (in seconds since // 1/1/70) + number of seconds to live OR indefinite if TimeToLive is // specified as LIVE_FOREVER, which is what we use if the host // information didn't originate from DNS // cacheEntry->ExpirationTime = (DWORD)((TimeToLive == LIVE_FOREVER) ? LIVE_FOREVER : time(NULL) + ((TimeToLive == LIVE_DEFAULT) ? DnsCacheTimeout : TimeToLive) ); // // the entry state is initially unused // cacheEntry->State = ENTRY_UNUSED; // // and reference is zero // cacheEntry->ReferenceCount = 0; } return cacheEntry; } #if INET_DEBUG // // CAVEAT - can only call these functions once per printf() etc. because of // static buffers (but still thread-safe) // PRIVATE DEBUG_FUNCTION LPSTR CacheTimestr(IN DWORD Time) { // // previous code - writes formatted human-sensible date/time to buffer // //LPSTR p; // //// //// remove the LF from the time string returned by ctime() //// // //p = ctime((const time_t *)&Time); //p[strlen(p) - 1] = '\0'; //return p; // // abbreviated CRT version - just write # seconds since 1970 to buffer // static char buf[16]; wsprintf(buf, "%d", Time); return (LPSTR)buf; } PRIVATE DEBUG_FUNCTION LPSTR CacheAddrInfoStr(IN LPADDRINFO AddrInfo) { static char buf[2048]; LPSTR p; int Count; p = buf; for (Count = 1; AddrInfo != NULL; AddrInfo = AddrInfo->ai_next) { p += wsprintf(p, "AddrInfo #%u:\n", Count++); p += wsprintf(p, "ai_flags = %u\n", AddrInfo->ai_flags); p += wsprintf(p, "ai_family = %u\n", AddrInfo->ai_family); p += wsprintf(p, "ai_socktype = %u\n", AddrInfo->ai_socktype); p += wsprintf(p, "ai_protocol = %u\n", AddrInfo->ai_protocol); p += wsprintf(p, "ai_addrlen = %u\n", AddrInfo->ai_addrlen); p += wsprintf(p, "ai_canonname = %s\n", AddrInfo->ai_canonname ? AddrInfo->ai_canonname : "NULL"); p += wsprintf(p, "ai_addr:\n%s\n", CacheMapSockAddress(AddrInfo->ai_addr)); } return (LPSTR)buf; } PRIVATE DEBUG_FUNCTION LPSTR CacheMapSockAddress(IN LPSOCKADDR Address) { if (!Address) { return ""; } static char buf[1024]; LPSTR p; p = buf; if (Address->sa_family == AF_INET) { struct sockaddr_in *sin; sin = (struct sockaddr_in *)Address; p += wsprintf(p, "sin_family = %u\n", sin->sin_family); p += wsprintf(p, "sin_port = %u\n", _I_ntohs(sin->sin_port)); p += wsprintf(p, "sin_addr = %s\n", _I_inet_ntoa(sin->sin_addr)); } else if (Address->sa_family == AF_INET6) { struct sockaddr_in6 *sin6; sin6 = (struct sockaddr_in6 *)Address; p += wsprintf(p, "sin6_family = %u\n", sin6->sin6_family); p += wsprintf(p, "sin6_port = %u\n", _I_ntohs(sin6->sin6_port)); p += wsprintf(p, "sin6_flowinfo = %u\n", sin6->sin6_flowinfo); p += wsprintf(p, "sin6_scope_id = %u\n", sin6->sin6_scope_id); p += wsprintf(p, "sin6_addr = %2x%2x:%2x%2x:%2x%2x:%2x%2x:%2x%2x:%2x%2x:%2x%2x:%2x%2x\n", sin6->sin6_addr.u.Byte[0], sin6->sin6_addr.u.Byte[1], sin6->sin6_addr.u.Byte[2], sin6->sin6_addr.u.Byte[3], sin6->sin6_addr.u.Byte[4], sin6->sin6_addr.u.Byte[5], sin6->sin6_addr.u.Byte[6], sin6->sin6_addr.u.Byte[7], sin6->sin6_addr.u.Byte[8], sin6->sin6_addr.u.Byte[9], sin6->sin6_addr.u.Byte[10], sin6->sin6_addr.u.Byte[11], sin6->sin6_addr.u.Byte[12], sin6->sin6_addr.u.Byte[13], sin6->sin6_addr.u.Byte[14], sin6->sin6_addr.u.Byte[15]); } else { p += wsprintf(p, "sa_family = %u\n", Address->sa_family); } return (LPSTR)buf; } #endif #if defined(RNR_SUPPORTED) /*++ Copyright (c) 1996 Microsoft Corporation Module Name: rescache.c Abstract: Contains name resolution cache Contents: Author: Shishir Pardikar 2-14-96 Environment: Win32 user mode Revision History: 2-14-96 shishirp Created --*/ // //BUGBUG: This include should be removed, duplicate of above // #ifndef SPX_SUPPORT #include #endif // // private manifests // #define NAMERES_CACHE_USED 0x00000001 #define NAMERES_CACHE_USES_GUID 0x00000002 #define ENTERCRIT_NAMERESCACHE() EnterCriticalSection(&vcritNameresCache) #define LEAVECRIT_NAMERESCACHE() LeaveCriticalSection(&vcritNameresCache) #define IS_EMPTY(indx) ((vlpNameresCache[(indx)].dwFlags & NAMERES_CACHE_USED) == 0) #define USES_GUID(indx) ((vlpNameresCache[(indx)].dwFlags & NAMERES_CACHE_USES_GUID)) // number of cache entries #define DEFAULT_NAMERES_CACHE_ENTRIES 10 // expiry time for an addresslist #define DEFAULT_EXPIRY_DELTA (24 * 60 * 60 * (LONGLONG)10000000) // // structure definition // typedef struct tagNAMERES_CACHE { DWORD dwFlags; // general flags to be used as needed DWORD dwNameSpace; // namespace ?? GUID sGuid; // GUID describing service type LPSTR lpszName; // ptr to name that needs resolution FILETIME ftLastUsedTime; // last accesstime, mainly for purging FILETIME ftCreationTime;// When it was created ADDRESS_INFO_LIST sAddrList; // List of address (defined in ixport.h) } NAMERES_CACHE, far *LPNAMERES_CACHE; // // private variables for name resolution cache // // Name cache size allocated in init LPNAMERES_CACHE vlpNameresCache = NULL; // Number of elements allowed in the nameres cache int vcntNameresCacheEntries = DEFAULT_NAMERES_CACHE_ENTRIES; // time in 100ns after which an address is expired LONGLONG vftExpiryDelta = DEFAULT_EXPIRY_DELTA; BOOL vfNameresCacheInited = FALSE; // serialization CRITICAL_SECTION vcritNameresCache; // // private function prototypes // PRIVATE DWORD CreateNameresCacheEntry( int indx, DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpszName, INT cntAddresses, LPCSADDR_INFO lpCsaddrInfo ); PRIVATE DWORD DeleteNameresCacheEntry( int indx ); PRIVATE int FindNameresCacheEntry( DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpszName ); PRIVATE int FindNameresCacheEntryByAddr( int cntAddr, LPCSADDR_INFO lpCsaddrInfo ); PRIVATE int PurgeEntries( BOOL fForce // purge atleast one entry ); PRIVATE DWORD CopyCsaddr( LPCSADDR_INFO lpSrc, int cntAddr, LPCSADDR_INFO *lplpDst ); // // functions // DWORD InitNameresCache( VOID ) /*++ Routine Description: Init name resolution cache. This routine a) allocates a table of name cache entries b) Arguments: None. Return Value: ERROR_SUCCESS if successful. --*/ { if (vfNameresCacheInited) { return (ERROR_SUCCESS); } // first try to alloc the memory, if it fails just quit vlpNameresCache = (LPNAMERES_CACHE)ALLOCATE_MEMORY( LPTR, vcntNameresCacheEntries * sizeof(NAMERES_CACHE) ); if (!vlpNameresCache) { return (ERROR_NOT_ENOUGH_MEMORY); } InitializeCriticalSection(&vcritNameresCache); ENTERCRIT_NAMERESCACHE(); vfNameresCacheInited = TRUE; LEAVECRIT_NAMERESCACHE(); return (ERROR_SUCCESS); } DWORD AddNameresCacheEntry( DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpName, int cntAddresses, LPCSADDR_INFO lpCsaddrInfo ) /*++ Routine Description: Arguments: Return Value: ERROR_SUCCESS if successful. --*/ { int indx; DWORD dwError = ERROR_SUCCESS; if (!vfNameresCacheInited) { return (ERROR_INVALID_PARAMETER); } ENTERCRIT_NAMERESCACHE(); indx = FindNameresCacheEntry(dwNameSpace, lpGuid, lpName); // if indx is valid, delete the entry, do some purging too if (indx != -1) { DeleteNameresCacheEntry(indx); PurgeEntries(FALSE); } else { // create atleast one hole indx = PurgeEntries(TRUE); } INET_ASSERT((indx >=0 && (indx < vcntNameresCacheEntries))); dwError = CreateNameresCacheEntry(indx, dwNameSpace, lpGuid, lpName, cntAddresses, lpCsaddrInfo); LEAVECRIT_NAMERESCACHE(); return (dwError); } DWORD RemoveNameresCacheEntry( DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpszName ) /*++ Routine Description: Arguments: Return Value: ERROR_SUCCESS if successful. --*/ { int indx; DWORD dwError = ERROR_INVALID_PARAMETER; if (vfNameresCacheInited) { ENTERCRIT_NAMERESCACHE(); indx = FindNameresCacheEntry(dwNameSpace, lpGuid, lpszName); if (indx != -1) { DeleteNameresCacheEntry(indx); dwError = ERROR_SUCCESS; } else { dwError = ERROR_FILE_NOT_FOUND; //yuk } LEAVECRIT_NAMERESCACHE(); } return (dwError); } #ifdef MAYBE DWORD RemoveNameresCacheEntryByAddr( int cntAddresses, LPCSADDR_INFO lpCsaddrInfo ) /*++ Routine Description: Arguments: Return Value: ERROR_SUCCESS if successful. --*/ { int indx; DWORD dwError = ERROR_INVALID_PARAMETER; if (vfNameresCacheInited) { ENTERCRIT_NAMERESCACHE(); indx = FindNameresCacheEntryByAddr(cntAddresses, lpCsaddrInfo); if (indx != -1) { DeleteNameresCacheEntry(indx); dwError = ERROR_SUCCESS; } else { dwError = ERROR_FILE_NOT_FOUND; } LEAVECRIT_NAMERESCACHE(); } return (dwError); } #endif //MAYBE DWORD GetNameresCacheEntry( DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpName, INT *lpcntAddresses, LPCSADDR_INFO *lplpCsaddrInfo ) /*++ Routine Description: This routine looks up the cache and returns the list of addresses corresponding to lpGuid/lpName. Arguments: Return Value: ERROR_SUCCESS if successful. --*/ { int indx; DWORD dwError = ERROR_FILE_NOT_FOUND; // poor error if (!vfNameresCacheInited) { return (ERROR_INVALID_PARAMETER); } ENTERCRIT_NAMERESCACHE(); // is this entry already cached? indx = FindNameresCacheEntry(dwNameSpace, lpGuid, lpName); if (indx != -1) { // yes, let use give back the info *lpcntAddresses = vlpNameresCache[indx].sAddrList.AddressCount; if ((dwError = CopyCsaddr(vlpNameresCache[indx].sAddrList.Addresses, *lpcntAddresses, lplpCsaddrInfo)) != ERROR_SUCCESS) { goto bailout; } // update the last used time, we will use this to // age out the entries GetCurrentGmtTime(&(vlpNameresCache[indx].ftLastUsedTime)); dwError = ERROR_SUCCESS; } bailout: LEAVECRIT_NAMERESCACHE(); return (dwError); } DWORD DeinitNameresCache( VOID ) /*++ Routine Description: Arguments: Return Value: ERROR_SUCCESS if successful. --*/ { int i; if (vfNameresCacheInited) { ENTERCRIT_NAMERESCACHE(); for (i = 0; i < vcntNameresCacheEntries; ++i) { if (!IS_EMPTY(i)) { DeleteNameresCacheEntry(i); } } FREE_MEMORY(vlpNameresCache); vlpNameresCache = NULL; vfNameresCacheInited = FALSE; LEAVECRIT_NAMERESCACHE(); DeleteCriticalSection(&vcritNameresCache); } return (ERROR_SUCCESS); } PRIVATE DWORD CreateNameresCacheEntry( int indx, DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpszName, int cntAddresses, LPCSADDR_INFO lpCsaddrInfo ) /*++ Routine Description: Arguments: Return Value: ERROR_SUCCESS if successful. --*/ { DWORD dwError = ERROR_NOT_ENOUGH_MEMORY; INET_ASSERT((indx >=0 && (indx < vcntNameresCacheEntries))); INET_ASSERT(IS_EMPTY(indx)); memset(&vlpNameresCache[indx], 0, sizeof(vlpNameresCache[indx])); // we could get a name or a guid // do it for name first before doing it for GUID // BUGBUG in future we should consider name+GUID+port if (lpszName) { vlpNameresCache[indx].lpszName = (LPSTR)ALLOCATE_MEMORY(LPTR, lstrlen(lpszName)+1); if (!vlpNameresCache[indx].lpszName) { goto bailout; } strcpy(vlpNameresCache[indx].lpszName, lpszName); } else if (lpGuid) { INET_ASSERT(FALSE); // rigth now. In future this should go away memcpy(&(vlpNameresCache[indx].sGuid), lpGuid, sizeof(GUID)); vlpNameresCache[indx].dwFlags |= NAMERES_CACHE_USES_GUID; } else { dwError = ERROR_INVALID_PARAMETER; goto bailout; } INET_ASSERT(cntAddresses > 0); if (CopyCsaddr(lpCsaddrInfo, cntAddresses, &(vlpNameresCache[indx].sAddrList.Addresses)) != ERROR_SUCCESS) { goto bailout; } vlpNameresCache[indx].sAddrList.AddressCount = cntAddresses; // mark this as being non-empty vlpNameresCache[indx].dwFlags |= NAMERES_CACHE_USED; // set the creation and last-used times as now GetCurrentGmtTime(&(vlpNameresCache[indx].ftCreationTime)); vlpNameresCache[indx].ftLastUsedTime = vlpNameresCache[indx].ftCreationTime ; dwError = ERROR_SUCCESS; bailout: if (dwError != ERROR_SUCCESS) { if (vlpNameresCache[indx].sAddrList.Addresses) { FREE_MEMORY(vlpNameresCache[indx].sAddrList.Addresses); vlpNameresCache[indx].sAddrList.Addresses = NULL; } if (vlpNameresCache[indx].lpszName) { FREE_MEMORY(vlpNameresCache[indx].lpszName); vlpNameresCache[indx].lpszName = NULL; } memset(&vlpNameresCache[indx], 0, sizeof(vlpNameresCache[indx])); } return (dwError); } PRIVATE DWORD DeleteNameresCacheEntry( int indx ) /*++ Routine Description: Arguments: Return Value: ERROR_SUCCESS if successful. --*/ { INET_ASSERT((indx >=0) && (indx < vcntNameresCacheEntries)); if (vlpNameresCache[indx].lpszName) { FREE_MEMORY(vlpNameresCache[indx].lpszName); } INET_ASSERT(vlpNameresCache[indx].sAddrList.Addresses); FREE_MEMORY(vlpNameresCache[indx].sAddrList.Addresses); memset(&vlpNameresCache[indx], 0, sizeof(NAMERES_CACHE)); return (ERROR_SUCCESS); } #ifdef MAYBE PRIVATE int FindNameresCacheEntryByAddr( int cntAddr, LPCSADDR_INFO lpCsaddrInfo ) /*++ Routine Description: Arguments: Return Value: ERROR_SUCCESS if successful. --*/ { int i; for (i = 0; i < vcntNameresCacheEntries; ++i) { if (!IS_EMPTY(i) && // not empty (vlpNameresCache[i].sAddrList.AddressCount == cntAddr) && // count is the same (!memcmp(vlpNameresCache[i].sAddrList.Addresses, // list matches lpCsaddrInfo, cntAddr * sizeof(CSADDR_INFO)))) { return (i); } } return (-1); } #endif //MAYBE PRIVATE int FindNameresCacheEntry( DWORD dwNameSpace, LPGUID lpGuid, LPSTR lpszName ) /*++ Routine Description: Arguments: Return Value: ERROR_SUCCESS if successful. --*/ { int i; for (i = 0; i < vcntNameresCacheEntries; ++i) { if (!IS_EMPTY(i)) { if (vlpNameresCache[i].dwNameSpace == dwNameSpace) { if (!USES_GUID(i)) { INET_ASSERT(vlpNameresCache[i].lpszName); if (lpszName && !lstrcmpi(lpszName, vlpNameresCache[i].lpszName)) { return (i); } } else{ if (lpGuid && !memcmp(lpGuid, &vlpNameresCache[i].sGuid, sizeof(GUID))) { return (i); } } } } } return (-1); } PRIVATE int PurgeEntries( BOOL fForce // purge atleast one entry ) /*++ Routine Description: Arguments: Return Value: index of a free entry --*/ { int i, indxlru = -1, indxHole=-1; FILETIME ft; BOOL fFoundHole = FALSE; GetCurrentGmtTime(&ft); for (i = 0; i < vcntNameresCacheEntries; ++i) { if (!IS_EMPTY(i)) { // purge stale entries if ( (FT2LL(ft) - FT2LL(vlpNameresCache[i].ftCreationTime)) > FT2LL(vftExpiryDelta)) { DeleteNameresCacheEntry(i); indxHole = i; } else if (FT2LL(vlpNameresCache[i].ftLastUsedTime) <= FT2LL(ft)) { ft = vlpNameresCache[i].ftLastUsedTime; indxlru = i; // LRU entry if we need to purge it } } else { indxHole = i; } } // if there is no hole, purge the LRU entry if forced if (indxHole == -1) { INET_ASSERT(indxlru != -1); if (fForce) { DeleteNameresCacheEntry(indxlru); indxHole = indxlru; } } return (indxHole); } PRIVATE DWORD CopyCsaddr( LPCSADDR_INFO lpSrc, int cntAddr, LPCSADDR_INFO *lplpDst ) { int i; LPCSADDR_INFO lpDst; UINT uSize; // BUGBUG assumes the way Compressaddress (ixport.cxx) allocates memory uSize = LocalSize(lpSrc); if (!uSize) { return (GetLastError()); } *lplpDst = (LPCSADDR_INFO)ALLOCATE_MEMORY(LPTR, uSize); if (!*lplpDst) { return (ERROR_NOT_ENOUGH_MEMORY); } lpDst = *lplpDst; memcpy(lpDst, lpSrc, uSize); // now start doing fixups for (i=0; i