Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2223 lines
49 KiB

/*++
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)