|
|
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name:
rescache.cxx
Abstract:
Contains functions which manipulate hostent cache for winsock gethostbyObject calls
Contents: QueryHostentCache CacheHostent FlushHostentCache ReleaseHostentCacheEntry ThrowOutHostentCacheEntry (RemoveCacheEntry) (ResolverCacheHit) (HostentMatch) (CreateCacheEntry) (CompareHostentNames) (BytesInHostent) (CopyHostentToBuffer)
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 HostentCacheInitialized = 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 LONG MaximumDnsCacheEntries = DEFAULT_DNS_CACHE_ENTRIES;
//
// 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 prototypes
//
PRIVATE VOID RemoveCacheEntry( IN SERIALIZED_LIST* pResolverCache, IN LPRESOLVER_CACHE_ENTRY lpCacheEntry );
PRIVATE BOOL ResolverCacheHit( IN LPRESOLVER_CACHE_ENTRY lpCacheEntry, IN LPSTR Name OPTIONAL, IN LPBYTE Address OPTIONAL );
PRIVATE BOOL HostentMatch( IN LPHOSTENT Hostent, IN LPSTR Name OPTIONAL, IN LPBYTE Address OPTIONAL );
PRIVATE LPRESOLVER_CACHE_ENTRY CreateCacheEntry( IN LPSTR lpszHostName, IN LPHOSTENT Hostent, IN DWORD TimeToLive, IN VOID** pAlloc=NULL, IN DWORD dwAllocSize=0 );
PRIVATE BOOL CompareHostentNames( IN LPHOSTENT Hostent, IN LPSTR Name );
PRIVATE DWORD BytesInHostent( IN LPHOSTENT Hostent );
PRIVATE DWORD CopyHostentToBuffer( OUT PCHAR Buffer, IN UINT BufferLength, IN PHOSTENT Hostent );
#if INET_DEBUG
PRIVATE DEBUG_FUNCTION LPSTR CacheTimestr( IN DWORD Time );
PRIVATE DEBUG_FUNCTION LPSTR CacheHostentStr( IN LPHOSTENT Hostent );
PRIVATE DEBUG_FUNCTION LPSTR CacheMapIpAddress( IN LPBYTE Address );
#endif
//
// functions
//
BOOL QueryHostentCache( SERIALIZED_LIST* pResolverCache, IN LPSTR Name OPTIONAL, IN LPBYTE Address OPTIONAL, OUT LPHOSTENT * Hostent, 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
Hostent - pointer to returned pointer to hostent
TimeToLive - pointer to returned time to live
Return Value:
BOOL
--*/
{ DEBUG_ENTER((DBG_SOCKETS, Bool, "QueryHostentCache", "%q, %s, %#x, %#x", Name, CacheMapIpAddress(Address), Hostent, TimeToLive ));
BOOL found;
if (!DnsCachingEnabled || !LockSerializedList(pResolverCache)) { DEBUG_PRINT(SOCKETS, WARNING, ("DNS caching disabled or unable to serialize access\n" ));
*Hostent = NULL; found = FALSE; goto quit; }
LPRESOLVER_CACHE_ENTRY cacheEntry; LPRESOLVER_CACHE_ENTRY previousEntry; DWORD timeNow;
cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(pResolverCache); timeNow = (DWORD)time(NULL);
while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache)) {
//
// 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->Hostent.h_name, CacheTimestr(cacheEntry->ExpirationTime) ));
//
// BUGBUG - what happens if ExpirationTime == timeNow?
//
previousEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Blink; RemoveCacheEntry(pResolverCache, 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
//
if (RemoveFromSerializedList(pResolverCache, &cacheEntry->ListEntry)) { if (InsertAtHeadOfSerializedList(pResolverCache, &cacheEntry->ListEntry)) { cacheEntry->State = ENTRY_IN_USE; ++cacheEntry->ReferenceCount; *Hostent = &cacheEntry->Hostent; found = TRUE;
DEBUG_PRINT(SOCKETS, INFO, ("entry found in DNS cache\n" )); } else { DEBUG_PRINT(SOCKETS, INFO, ("entry found in DNS cache, but removed due to not enough memory\n" )); } }
goto done; } cacheEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Flink; }
*Hostent = NULL; found = FALSE;
DEBUG_PRINT(SOCKETS, INFO, ("didn't find entry in DNS cache\n" ));
done:
UnlockSerializedList(pResolverCache);
quit:
DEBUG_LEAVE(found);
return found; }
VOID CacheHostent( SERIALIZED_LIST* pResolverCache, IN LPSTR lpszHostName, IN LPHOSTENT Hostent, IN DWORD TimeToLive, IN VOID** pAlloc, IN DWORD dwAllocSize )
/*++
Routine Description:
Adds a hostent structure to the cache. Creates a new structure and links it into the cache list, displacing the LRU entry if required. If we cannot create the entry, no action is taken, no errors returned
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"
Hostent - pointer to hostent to add to 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, "CacheHostent", "%q, %#x, %d", lpszHostName, Hostent, TimeToLive ));
if (!DnsCachingEnabled || !LockSerializedList(pResolverCache)) { DEBUG_PRINT(SOCKETS, WARNING, ("DNS caching disabled or unable to serialize access\n" ));
goto quit; }
//
// check that the entry is not already in the cache - 2 or more threads may
// have been simultaneously resolving the same name
//
LPHOSTENT lpHostent; DWORD ttl;
INET_ASSERT(lpszHostName != NULL);
if (!QueryHostentCache(pResolverCache, lpszHostName, NULL, &lpHostent, &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(pResolverCache);
while ((pResolverCache->ElementCount >= MaximumDnsCacheEntries) && (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache))) {
//
// 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->Hostent.h_name ));
LPRESOLVER_CACHE_ENTRY nextEntry;
nextEntry = (LPRESOLVER_CACHE_ENTRY)cacheEntry->ListEntry.Flink; RemoveCacheEntry(pResolverCache, 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, Hostent, TimeToLive, pAlloc, dwAllocSize)) {
DEBUG_PRINT(SOCKETS, INFO, ("caching %q, expiry = %s\n", CacheHostentStr(&cacheEntry->Hostent), CacheTimestr(cacheEntry->ExpirationTime) ));
if (!InsertAtHeadOfSerializedList(pResolverCache, &cacheEntry->ListEntry)) { DEBUG_PRINT(SOCKETS, INFO, ("Unable to cache new entry due to not enough memory\n" )); } } } 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", Hostent->h_name ));
ReleaseHostentCacheEntry(pResolverCache, lpHostent);
}
UnlockSerializedList(pResolverCache);
quit:
DEBUG_LEAVE(0); }
VOID FlushHostentCache( SERIALIZED_LIST* pResolverCache )
/*++
Routine Description:
Removes all entries in DNS hostent cache
Arguments:
None.
Return Value:
None.
--*/
{ DEBUG_ENTER((DBG_SOCKETS, None, "FlushHostentCache", NULL ));
LPRESOLVER_CACHE_ENTRY cacheEntry; LPRESOLVER_CACHE_ENTRY previousEntry;
if (LockSerializedList(pResolverCache)) { previousEntry = (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache); cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(pResolverCache); while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache)) { if (cacheEntry->State == ENTRY_UNUSED) { RemoveCacheEntry(pResolverCache, 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(pResolverCache); }
DEBUG_LEAVE(0); }
VOID ReleaseHostentCacheEntry( SERIALIZED_LIST* pResolverCache, IN LPHOSTENT lpHostent )
/*++
Routine Description:
Either mark a hostent unused or if it is stale, delete it
Arguments:
lpHostent - pointer to hostent to free
Return Value:
None.
--*/
{ DEBUG_ENTER((DBG_SOCKETS, None, "ReleaseHostentCacheEntry", "%#x", lpHostent ));
LPRESOLVER_CACHE_ENTRY cacheEntry = CONTAINING_RECORD(lpHostent, RESOLVER_CACHE_ENTRY, Hostent );
if (LockSerializedList(pResolverCache)) {
//
// 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(pResolverCache, 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(pResolverCache); }
DEBUG_LEAVE(0); }
VOID ThrowOutHostentCacheEntry( SERIALIZED_LIST* pResolverCache, IN LPHOSTENT lpHostent )
/*++
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:
lpHostent - pointer to host entry containing details (name) of entry to throw out
Return Value:
None.
--*/
{ DEBUG_ENTER((DBG_SOCKETS, None, "ThrowOutHostentCacheEntry", "%#x [%q]", lpHostent, lpHostent->h_name ));
if (DnsCachingEnabled && LockSerializedList(pResolverCache)) {
LPRESOLVER_CACHE_ENTRY cacheEntry;
cacheEntry = (LPRESOLVER_CACHE_ENTRY)HeadOfSerializedList(pResolverCache); while (cacheEntry != (LPRESOLVER_CACHE_ENTRY)SlSelf(pResolverCache)) { if (HostentMatch(&cacheEntry->Hostent, lpHostent->h_name, 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(pResolverCache, cacheEntry); } else { cacheEntry->State = ENTRY_DELETE; } break; } }
UnlockSerializedList(pResolverCache); } else {
DEBUG_PRINT(SOCKETS, WARNING, ("DNS caching disabled or unable to serialize access\n" ));
}
DEBUG_LEAVE(0); }
PRIVATE VOID RemoveCacheEntry( SERIALIZED_LIST* pResolverCache, 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 ));
if (RemoveFromSerializedList(pResolverCache, &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", CacheHostentStr(&lpCacheEntry->Hostent), CacheTimestr(lpCacheEntry->ExpirationTime) ));
lpCacheEntry = (LPRESOLVER_CACHE_ENTRY)FREE_MEMORY((HLOCAL)lpCacheEntry);
INET_ASSERT(lpCacheEntry == NULL);
DEBUG_PRINT(SOCKETS, INFO, ("CurrentDnsCacheEntries = %d\n", pResolverCache->ElementCount ));
INET_ASSERT((pResolverCache->ElementCount >= 0) && (pResolverCache->ElementCount <= MaximumDnsCacheEntries)); } else { DEBUG_PRINT(SOCKETS, INFO, ("unable to throw out %q due to insufficient resources, expiry = %s\n", CacheHostentStr(&lpCacheEntry->Hostent), CacheTimestr(lpCacheEntry->ExpirationTime) )); }
DEBUG_LEAVE(0); }
PRIVATE BOOL ResolverCacheHit( IN LPRESOLVER_CACHE_ENTRY lpCacheEntry, IN LPSTR Name OPTIONAL, IN LPBYTE Address OPTIONAL )
/*++
Routine Description:
Checks this RESOLVER_CACHE_ENTRY for a match with Name or Address. If Name, can match with name or alias(es) in hostent, 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, CacheMapIpAddress(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; } if (!found) { found = HostentMatch(&lpCacheEntry->Hostent, Name, Address); }
DEBUG_LEAVE(found);
return found; }
PRIVATE BOOL HostentMatch( IN LPHOSTENT Hostent, IN LPSTR Name OPTIONAL, IN LPBYTE Address OPTIONAL )
/*++
Routine Description:
Compares a hostent structure for a match with a host name or address
Arguments:
Hostent - pointer to hostent to compare
Name - pointer to name string
Address - pointer to IP address in network byte order
Return Value:
BOOL
--*/
{ DEBUG_ENTER((DBG_SOCKETS, Bool, "HostentMatch", "%#x, %q, %s", Hostent, Name, CacheMapIpAddress(Address) ));
BOOL found;
if (Name) { found = CompareHostentNames(Hostent, Name); } else {
LPBYTE* addressList = (LPBYTE*)Hostent->h_addr_list; LPBYTE address;
found = FALSE;
while (address = *addressList++) { if (*(LPDWORD)address == *(LPDWORD)Address) {
DEBUG_PRINT(SOCKETS, INFO, ("matched %s\n", CacheMapIpAddress(address) ));
found = TRUE; break; } } }
if (found) {
DEBUG_PRINT(SOCKETS, INFO, ("hostent = %q\n", CacheHostentStr(Hostent) ));
}
DEBUG_LEAVE(found);
return found; }
PRIVATE LPRESOLVER_CACHE_ENTRY CreateCacheEntry( IN LPSTR lpszHostName, IN LPHOSTENT Hostent, IN DWORD TimeToLive, IN VOID** pAlloc, IN DWORD dwAllocSize )
/*++
Routine Description:
Allocates a RESOLVER_CACHE_ENTRY and packs it with the hostent information and sets the ExpirationTime
Arguments:
lpszHostName - name we resolved
Hostent - pointer to hostent to add
TimeToLive - amount of time before this hostent expires
Return Value:
LPRESOLVER_CACHE_ENTRY
--*/
{ LPRESOLVER_CACHE_ENTRY cacheEntry; UINT hostentSize;
//
// BytesInHostent gives us the size of the fixed and variable parts of the
// hostent structure
//
hostentSize = (UINT)BytesInHostent(Hostent);
INET_ASSERT(lpszHostName != NULL);
//
// only copy lpszHostName if it is different to the names in the hostent
//
UINT hostNameSize;
if (!CompareHostentNames(Hostent, lpszHostName)) { hostNameSize = lstrlen(lpszHostName) + 1; } else { hostNameSize = 0; }
//
// allocate space for the cache entry (take off the size of the fixed part
// of the hostent - BytesInHostent already accounted for it)
//
DWORD dwSize = sizeof(RESOLVER_CACHE_ENTRY) - sizeof(HOSTENT) + hostentSize + hostNameSize;
if (dwSize <= dwAllocSize) { cacheEntry = (LPRESOLVER_CACHE_ENTRY)(*pAlloc); *pAlloc = NULL; } else { cacheEntry = (LPRESOLVER_CACHE_ENTRY)ALLOCATE_MEMORY(LMEM_FIXED, dwSize ); } if (cacheEntry != NULL) { CopyHostentToBuffer((PCHAR)&cacheEntry->Hostent, hostentSize, Hostent);
//
// copy the host name to the end of the buffer if required
//
if (hostNameSize != 0) { cacheEntry->HostName = (LPSTR)&cacheEntry->Hostent + hostentSize; 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; }
PRIVATE BOOL CompareHostentNames( IN LPHOSTENT Hostent, IN LPSTR Name )
/*++
Routine Description:
Compares a prospective host name against all names in a hostent
Arguments:
Hostent - pointer to hostent containing names to compare
Name - prospective host name
Return Value:
BOOL
--*/
{ DEBUG_ENTER((DBG_SOCKETS, Bool, "CompareHostentNames", "%#x, %q", Hostent, Name ));
BOOL found;
if (!lstrcmpi(Hostent->h_name, Name)) {
DEBUG_PRINT(SOCKETS, INFO, ("matched name %q\n", Hostent->h_name ));
found = TRUE; goto done; }
LPSTR alias; LPSTR* aliasList;
aliasList = Hostent->h_aliases; while (alias = *aliasList++) { if (!lstrcmpi(alias, Name)) {
DEBUG_PRINT(SOCKETS, INFO, ("matched alias %q\n", alias ));
found = TRUE; goto done; } }
DEBUG_PRINT(SOCKETS, WARNING, ("%q not matched\n", Name ));
found = FALSE;
done:
DEBUG_LEAVE(found);
return found; }
PRIVATE DWORD BytesInHostent( IN LPHOSTENT Hostent ) { DWORD total; int i;
total = sizeof(HOSTENT); total += lstrlen(Hostent->h_name) + 1;
//
// Account for the NULL terminator pointers at the end of the
// alias and address arrays.
//
total += sizeof(char *) + sizeof(char *);
for (i = 0; Hostent->h_aliases[i] != NULL; i++) { total += lstrlen(Hostent->h_aliases[i]) + 1 + sizeof(char *); }
for (i = 0; Hostent->h_addr_list[i] != NULL; i++) { total += Hostent->h_length + sizeof(char *); }
//
// Pad the answer to an eight-byte boundary.
//
return (total + 7) & ~7; }
PRIVATE DWORD CopyHostentToBuffer ( OUT PCHAR Buffer, IN UINT BufferLength, IN LPHOSTENT Hostent ) { UINT requiredBufferLength; UINT bytesFilled; PCHAR currentLocation = Buffer; UINT aliasCount; UINT addressCount; UINT i; PHOSTENT outputHostent = (PHOSTENT)Buffer;
//
// Determine how many bytes are needed to fully copy the structure.
//
requiredBufferLength = (UINT)BytesInHostent(Hostent);
//
// Zero the user buffer.
//
if ( (DWORD)BufferLength > requiredBufferLength ) { RtlZeroMemory( Buffer, requiredBufferLength ); } else { RtlZeroMemory( Buffer, BufferLength ); }
//
// Copy over the hostent structure if it fits.
//
bytesFilled = sizeof(*Hostent);
if ( bytesFilled > (DWORD)BufferLength ) { return requiredBufferLength; }
RtlCopyMemory( currentLocation, Hostent, sizeof(*Hostent) ); currentLocation = Buffer + bytesFilled;
outputHostent->h_name = NULL; outputHostent->h_aliases = NULL; outputHostent->h_addr_list = NULL;
//
// Count the host's aliases and set up an array to hold pointers to
// them.
//
for ( aliasCount = 0; Hostent->h_aliases[aliasCount] != NULL; aliasCount++ );
bytesFilled += (aliasCount+1) * sizeof(char FAR *);
if ( bytesFilled > (DWORD)BufferLength ) { Hostent->h_aliases = NULL; return requiredBufferLength; }
outputHostent->h_aliases = (char FAR * FAR *)currentLocation; currentLocation = Buffer + bytesFilled;
//
// Count the host's addresses and set up an array to hold pointers to
// them.
//
for ( addressCount = 0; Hostent->h_addr_list[addressCount] != NULL; addressCount++ );
bytesFilled += (addressCount+1) * sizeof(void FAR *);
if ( bytesFilled > (DWORD)BufferLength ) { Hostent->h_addr_list = NULL; return requiredBufferLength; }
outputHostent->h_addr_list = (char FAR * FAR *)currentLocation; currentLocation = Buffer + bytesFilled;
//
// Start filling in addresses. Do addresses before filling in the
// host name and aliases in order to avoid alignment problems.
//
for ( i = 0; i < addressCount; i++ ) {
bytesFilled += Hostent->h_length;
if ( bytesFilled > (DWORD)BufferLength ) { outputHostent->h_addr_list[i] = NULL; return requiredBufferLength; }
outputHostent->h_addr_list[i] = currentLocation;
RtlCopyMemory( currentLocation, Hostent->h_addr_list[i], Hostent->h_length );
currentLocation = Buffer + bytesFilled; }
outputHostent->h_addr_list[i] = NULL;
//
// Copy the host name if it fits.
//
bytesFilled += lstrlen(Hostent->h_name) + 1;
if ( bytesFilled > (DWORD)BufferLength ) { return requiredBufferLength; }
outputHostent->h_name = currentLocation;
RtlCopyMemory(currentLocation, Hostent->h_name, lstrlen(Hostent->h_name) + 1); currentLocation = Buffer + bytesFilled;
//
// Start filling in aliases.
//
for ( i = 0; i < aliasCount; i++ ) {
bytesFilled += lstrlen(Hostent->h_aliases[i]) + 1;
if ( bytesFilled > (DWORD)BufferLength ) { outputHostent->h_aliases[i] = NULL; return requiredBufferLength; }
outputHostent->h_aliases[i] = currentLocation;
RtlCopyMemory( currentLocation, Hostent->h_aliases[i], lstrlen(Hostent->h_aliases[i]) + 1 );
currentLocation = Buffer + bytesFilled; }
outputHostent->h_aliases[i] = NULL;
return requiredBufferLength; }
#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 CacheHostentStr(IN LPHOSTENT Hostent) {
static char buf[1024]; LPSTR p;
p = buf; p += wsprintf(p, "n=%s", Hostent->h_name);
for (LPSTR * aliases = (LPSTR *)Hostent->h_aliases; *aliases; ++aliases) { p += wsprintf(p, ", a=%s", *aliases); }
for (LPBYTE * addrs = (LPBYTE *)Hostent->h_addr_list; *addrs; ++addrs) { p += wsprintf(p, ", i=%s", CacheMapIpAddress(*addrs) ); }
return (LPSTR)buf; }
PRIVATE DEBUG_FUNCTION LPSTR CacheMapIpAddress(IN LPBYTE Address) {
if (!Address) { return ""; }
static char buf[16];
wsprintf(buf, "%d.%d.%d.%d", Address[0] & 0xff, Address[1] & 0xff, Address[2] & 0xff, Address[3] & 0xff );
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 <wininetp.h>
#endif
//
// private manifests
//
#define NAMERES_CACHE_USED 0x00000001
#define NAMERES_CACHE_USES_GUID 0x00000002
#define ENTERCRIT_NAMERESCACHE() (vcritNameresCache.Lock())
#define LEAVECRIT_NAMERESCACHE() (vcritNameresCache.Unlock())
#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
CCritSec 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); }
if (!vcritNameresCache.Init() || !ENTERCRIT_NAMERESCACHE()) { FREE_MEMORY(vlpNameresCache); return ERROR_NOT_ENOUGH_MEMORY; }
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); }
if (!ENTERCRIT_NAMERESCACHE()) { return ERROR_NOT_ENOUGH_MEMORY; }
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) {
if (!ENTERCRIT_NAMERESCACHE()) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto quit; }
indx = FindNameresCacheEntry(dwNameSpace, lpGuid, lpszName);
if (indx != -1) {
DeleteNameresCacheEntry(indx);
dwError = ERROR_SUCCESS; } else { dwError = ERROR_FILE_NOT_FOUND; //yuk
}
LEAVECRIT_NAMERESCACHE(); } quit: 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) { if (!ENTERCRIT_NAMERESCACHE()) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto quit; }
indx = FindNameresCacheEntryByAddr(cntAddresses, lpCsaddrInfo);
if (indx != -1) {
DeleteNameresCacheEntry(indx);
dwError = ERROR_SUCCESS; } else { dwError = ERROR_FILE_NOT_FOUND; }
LEAVECRIT_NAMERESCACHE(); } quit: 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); }
if (!ENTERCRIT_NAMERESCACHE()) { dwError = ERROR_NOT_ENOUGH_MEMORY; goto quit; }
// 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();
quit: return (dwError); }
DWORD DeinitNameresCache( VOID ) /*++
Routine Description:
Arguments:
Return Value:
ERROR_SUCCESS if successful.
--*/ { int i;
if (vfNameresCacheInited) { if (!ENTERCRIT_NAMERESCACHE()) { return ERROR_NOT_ENOUGH_MEMORY; }
for (i = 0; i < vcntNameresCacheEntries; ++i) { if (!IS_EMPTY(i)) { DeleteNameresCacheEntry(i); } }
FREE_MEMORY(vlpNameresCache);
vlpNameresCache = NULL;
vfNameresCacheInited = FALSE;
LEAVECRIT_NAMERESCACHE(); vcritNameresCache.FreeLock(); } 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<cntAddr; ++i) { lpDst[i].LocalAddr.lpSockaddr = (LPSOCKADDR)((LPBYTE)lpDst+((DWORD)(lpSrc[i].LocalAddr.lpSockaddr) - (DWORD)lpSrc)); lpDst[i].RemoteAddr.lpSockaddr = (LPSOCKADDR)((LPBYTE)lpDst+((DWORD)(lpSrc[i].RemoteAddr.lpSockaddr) - (DWORD)lpSrc)); } return (ERROR_SUCCESS); }
#endif // defined(RNR_SUPPORTED)
|